diff --git a/doc/classes/EditorSettings.xml b/doc/classes/EditorSettings.xml index 6af096793..fb7a6d070 100644 --- a/doc/classes/EditorSettings.xml +++ b/doc/classes/EditorSettings.xml @@ -836,6 +836,9 @@ If [code]true[/code], sorts the members outline (located at the left of the script editor) using alphabetical order. If [code]false[/code], sorts the members outline depending on the order in which members are found in the script. [b]Note:[/b] Only effective if [member text_editor/script_list/show_members_overview] is [code]true[/code]. + + Highlight the currently selected TileMapLayer by dimming the other ones in the scene. + diff --git a/modules/layered_tile_map/config.py b/modules/layered_tile_map/config.py index 360ca1f42..344350606 100644 --- a/modules/layered_tile_map/config.py +++ b/modules/layered_tile_map/config.py @@ -9,7 +9,7 @@ def get_doc_classes(): return [ "LayeredTileData", "LayeredTileMap", - "LayeredTileMapLayerGroup", + "LayeredTileMapLayer", "LayeredTileMapPattern", "LayeredTileSet", "LayeredTileSetAtlasSource", diff --git a/modules/layered_tile_map/doc_classes/LayeredTileMap.xml b/modules/layered_tile_map/doc_classes/LayeredTileMap.xml index 2df398050..6e5f6bf6c 100644 --- a/modules/layered_tile_map/doc_classes/LayeredTileMap.xml +++ b/modules/layered_tile_map/doc_classes/LayeredTileMap.xml @@ -1,5 +1,5 @@ - + Node for 2D tile-based maps. @@ -89,7 +89,8 @@ - Returns the tile alternative ID of the cell on layer [param layer] at [param coords]. If [param use_proxies] is [code]false[/code], ignores the [LayeredTileSet]'s tile proxies, returning the raw alternative identifier. See [method LayeredTileSet.map_tile_proxy]. + Returns the tile alternative ID of the cell on layer [param layer] at [param coords]. + If [param use_proxies] is [code]false[/code], ignores the [TileSet]'s tile proxies, returning the raw alternative identifier. See [method TileSet.map_tile_proxy]. If [param layer] is negative, the layers are accessed from the last one. @@ -99,7 +100,8 @@ - Returns the tile atlas coordinates ID of the cell on layer [param layer] at coordinates [param coords]. If [param use_proxies] is [code]false[/code], ignores the [LayeredTileSet]'s tile proxies, returning the raw alternative identifier. See [method LayeredTileSet.map_tile_proxy]. + Returns the tile atlas coordinates ID of the cell on layer [param layer] at coordinates [param coords]. Returns [code]Vector2i(-1, -1)[/code] if the cell does not exist. + If [param use_proxies] is [code]false[/code], ignores the [TileSet]'s tile proxies, returning the raw atlas coordinate identifier. See [method TileSet.map_tile_proxy]. If [param layer] is negative, the layers are accessed from the last one. @@ -110,7 +112,7 @@ Returns the tile source ID of the cell on layer [param layer] at coordinates [param coords]. Returns [code]-1[/code] if the cell does not exist. - If [param use_proxies] is [code]false[/code], ignores the [LayeredTileSet]'s tile proxies, returning the raw alternative identifier. See [method LayeredTileSet.map_tile_proxy]. + If [param use_proxies] is [code]false[/code], ignores the [TileSet]'s tile proxies, returning the raw source identifier. See [method TileSet.map_tile_proxy]. If [param layer] is negative, the layers are accessed from the last one. @@ -122,7 +124,6 @@ Returns the [LayeredTileData] object associated with the given cell, or [code]null[/code] if the cell does not exist or is not a [LayeredTileSetAtlasSource]. If [param layer] is negative, the layers are accessed from the last one. - If [param use_proxies] is [code]false[/code], ignores the [LayeredTileSet]'s tile proxies, returning the raw alternative identifier. See [method LayeredTileSet.map_tile_proxy]. [codeblock] func get_clicked_tile_power(): var clicked_cell = tile_map.local_to_map(tile_map.get_local_mouse_position()) @@ -132,6 +133,7 @@ else: return 0 [/codeblock] + If [param use_proxies] is [code]false[/code], ignores the [TileSet]'s tile proxies. See [method TileSet.map_tile_proxy]. @@ -483,6 +485,9 @@ Show or hide the LayeredTileMap's navigation meshes. If set to [constant VISIBILITY_MODE_DEFAULT], this depends on the show navigation debug settings. + + The [TileSet] used by this [TileMap]. The textures, collisions, and additional behavior of all available tiles are stored here. + diff --git a/modules/layered_tile_map/doc_classes/LayeredTileMapLayer.xml b/modules/layered_tile_map/doc_classes/LayeredTileMapLayer.xml new file mode 100644 index 000000000..e62c19182 --- /dev/null +++ b/modules/layered_tile_map/doc_classes/LayeredTileMapLayer.xml @@ -0,0 +1,303 @@ + + + + Node for 2D tile-based maps. + + + Node for 2D tile-based maps. A [TileMapLayer] uses a [TileSet] which contain a list of tiles which are used to create grid-based maps. Unlike the [TileMap] node, which is deprecated, [TileMapLayer] has only one layer of tiles. You can use several [TileMapLayer] to achieve the same result as a [TileMap] node. + For performance reasons, all TileMap updates are batched at the end of a frame. Notably, this means that scene tiles from a [TileSetScenesCollectionSource] may be initialized after their parent. This is only queued when inside the scene tree. + To force an update earlier on, call [method update_internals]. + + + + + + + + + + Called with a [TileData] object about to be used internally by the [TileMapLayer], allowing its modification at runtime. + This method is only called if [method _use_tile_data_runtime_update] is implemented and returns [code]true[/code] for the given tile [param coords]. + [b]Warning:[/b] The [param tile_data] object's sub-resources are the same as the one in the TileSet. Modifying them might impact the whole TileSet. Instead, make sure to duplicate those resources. + [b]Note:[/b] If the properties of [param tile_data] object should change over time, use [method notify_runtime_tile_data_update] to notify the [TileMapLayer] it needs an update. + + + + + + + Should return [code]true[/code] if the tile at coordinates [param coords] requires a runtime update. + [b]Warning:[/b] Make sure this function only returns [code]true[/code] when needed. Any tile processed at runtime without a need for it will imply a significant performance penalty. + [b]Note:[/b] If the result of this function should change, use [method notify_runtime_tile_data_update] to notify the [TileMapLayer] it needs an update. + + + + + + Clears all cells. + + + + + + + Erases the cell at coordinates [param coords]. + + + + + + Clears cells containing tiles that do not exist in the [member tile_set]. + + + + + + + Returns the tile alternative ID of the cell at coordinates [param coords]. + + + + + + + Returns the tile atlas coordinates ID of the cell at coordinates [param coords]. Returns [code]Vector2i(-1, -1)[/code] if the cell does not exist. + + + + + + + Returns the tile source ID of the cell at coordinates [param coords]. Returns [code]-1[/code] if the cell does not exist. + + + + + + + Returns the [TileData] object associated with the given cell, or [code]null[/code] if the cell does not exist or is not a [TileSetAtlasSource]. + [codeblock] + func get_clicked_tile_power(): + var clicked_cell = tile_map_layer.local_to_map(tile_map_layer.get_local_mouse_position()) + var data = tile_map_layer.get_cell_tile_data(clicked_cell) + if data: + return data.get_custom_data("power") + else: + return 0 + [/codeblock] + + + + + + + Returns the coordinates of the tile for given physics body [RID]. Such an [RID] can be retrieved from [method KinematicCollision2D.get_collider_rid], when colliding with a tile. + + + + + + Returns the [RID] of the [NavigationServer2D] navigation used by this [TileMapLayer]. + By default this returns the default [World2D] navigation map, unless a custom map was provided using [method set_navigation_map]. + + + + + + + + Returns the neighboring cell to the one at coordinates [param coords], identified by the [param neighbor] direction. This method takes into account the different layouts a TileMap can take. + + + + + + + Creates and returns a new [TileMapPattern] from the given array of cells. See also [method set_pattern]. + + + + + + + Returns the list of all neighboring cells to the one at [param coords]. + + + + + + Returns a [Vector2i] array with the positions of all cells containing a tile. A cell is considered empty if its source identifier equals [code]-1[/code], its atlas coordinate identifier is [code]Vector2(-1, -1)[/code] and its alternative identifier is [code]-1[/code]. + + + + + + + + + Returns a [Vector2i] array with the positions of all cells containing a tile. Tiles may be filtered according to their source ([param source_id]), their atlas coordinates ([param atlas_coords]), or alternative id ([param alternative_tile]). + If a parameter has its value set to the default one, this parameter is not used to filter a cell. Thus, if all parameters have their respective default values, this method returns the same result as [method get_used_cells]. + A cell is considered empty if its source identifier equals [code]-1[/code], its atlas coordinate identifier is [code]Vector2(-1, -1)[/code] and its alternative identifier is [code]-1[/code]. + + + + + + Returns a rectangle enclosing the used (non-empty) tiles of the map. + + + + + + + Returns whether the provided [param body] [RID] belongs to one of this [TileMapLayer]'s cells. + + + + + + + Returns the map coordinates of the cell containing the given [param local_position]. If [param local_position] is in global coordinates, consider using [method Node2D.to_local] before passing it to this method. See also [method map_to_local]. + + + + + + + + + Returns for the given coordinates [param coords_in_pattern] in a [TileMapPattern] the corresponding cell coordinates if the pattern was pasted at the [param position_in_tilemap] coordinates (see [method set_pattern]). This mapping is required as in half-offset tile shapes, the mapping might not work by calculating [code]position_in_tile_map + coords_in_pattern[/code]. + + + + + + + Returns the centered position of a cell in the [TileMapLayer]'s local coordinate space. To convert the returned value into global coordinates, use [method Node2D.to_global]. See also [method local_to_map]. + [b]Note:[/b] This may not correspond to the visual position of the tile, i.e. it ignores the [member TileData.texture_origin] property of individual tiles. + + + + + + Notifies the [TileMapLayer] node that calls to [method _use_tile_data_runtime_update] or [method _tile_data_runtime_update] will lead to different results. This will thus trigger a [TileMapLayer] update. + [b]Warning:[/b] Updating the [TileMapLayer] is computationally expensive and may impact performance. Try to limit the number of calls to this function to avoid unnecessary update. + [b]Note:[/b] This does not trigger a direct update of the [TileMapLayer], the update will be done at the end of the frame as usual (unless you call [method update_internals]). + + + + + + + + + + Sets the tile identifiers for the cell at coordinates [param coords]. Each tile of the [TileSet] is identified using three parts: + - The source identifier [param source_id] identifies a [TileSetSource] identifier. See [method TileSet.set_source_id], + - The atlas coordinate identifier [param atlas_coords] identifies a tile coordinates in the atlas (if the source is a [TileSetAtlasSource]). For [TileSetScenesCollectionSource] it should always be [code]Vector2i(0, 0)[/code], + - The alternative tile identifier [param alternative_tile] identifies a tile alternative in the atlas (if the source is a [TileSetAtlasSource]), and the scene for a [TileSetScenesCollectionSource]. + If [param source_id] is set to [code]-1[/code], [param atlas_coords] to [code]Vector2i(-1, -1)[/code], or [param alternative_tile] to [code]-1[/code], the cell will be erased. An erased cell gets [b]all[/b] its identifiers automatically set to their respective invalid values, namely [code]-1[/code], [code]Vector2i(-1, -1)[/code] and [code]-1[/code]. + + + + + + + + + + Update all the cells in the [param cells] coordinates array so that they use the given [param terrain] for the given [param terrain_set]. If an updated cell has the same terrain as one of its neighboring cells, this function tries to join the two. This function might update neighboring tiles if needed to create correct terrain transitions. + If [param ignore_empty_terrains] is true, empty terrains will be ignored when trying to find the best fitting tile for the given terrain constraints. + [b]Note:[/b] To work correctly, this method requires the [TileMapLayer]'s TileSet to have terrains set up with all required terrain combinations. Otherwise, it may produce unexpected results. + + + + + + + + + + Update all the cells in the [param path] coordinates array so that they use the given [param terrain] for the given [param terrain_set]. The function will also connect two successive cell in the path with the same terrain. This function might update neighboring tiles if needed to create correct terrain transitions. + If [param ignore_empty_terrains] is true, empty terrains will be ignored when trying to find the best fitting tile for the given terrain constraints. + [b]Note:[/b] To work correctly, this method requires the [TileMapLayer]'s TileSet to have terrains set up with all required terrain combinations. Otherwise, it may produce unexpected results. + + + + + + + Sets a custom [param map] as a [NavigationServer2D] navigation map. If not set, uses the default [World2D] navigation map instead. + + + + + + + + Pastes the [TileMapPattern] at the given [param position] in the tile map. See also [method get_pattern]. + + + + + + Triggers a direct update of the [TileMapLayer]. Usually, calling this function is not needed, as [TileMapLayer] node updates automatically when one of its properties or cells is modified. + However, for performance reasons, those updates are batched and delayed to the end of the frame. Calling this function will force the [TileMapLayer] to update right away instead. + [b]Warning:[/b] Updating the [TileMapLayer] is computationally expensive and may impact performance. Try to limit the number of updates and how many tiles they impact. + + + + + + Enable or disable collisions. + + + Show or hide the [TileMapLayer]'s collision shapes. If set to [constant DEBUG_VISIBILITY_MODE_DEFAULT], this depends on the show collision debug settings. + + + If [code]false[/code], disables this [TileMapLayer] completely (rendering, collision, navigation, scene tiles, etc.) + + + If [code]true[/code], navigation regions are enabled. + + + Show or hide the [TileMapLayer]'s navigation meshes. If set to [constant DEBUG_VISIBILITY_MODE_DEFAULT], this depends on the show navigation debug settings. + + + The [TileMapLayer]'s quadrant size. A quadrant is a group of tiles to be drawn together on a single canvas item, for optimization purposes. [member rendering_quadrant_size] defines the length of a square's side, in the map's coordinate system, that forms the quadrant. Thus, the default quandrant size groups together [code]16 * 16 = 256[/code] tiles. + The quadrant size does not apply on a Y-sorted [TileMapLayer], as tiles are be grouped by Y position instead in that case. + [b]Note:[/b] As quadrants are created according to the map's coordinate system, the quadrant's "square shape" might not look like square in the [TileMapLayer]'s local coordinate system. + + + The raw tile map data as a byte array. + + + The [TileSet] used by this layer. The textures, collisions, and additional behavior of all available tiles are stored here. + + + If [code]true[/code], this [TileMapLayer] collision shapes will be instantiated as kinematic bodies. This can be needed for moving [TileMapLayer] nodes (i.e. moving platforms). + + + This Y-sort origin value is added to each tile's Y-sort origin value. This allows, for example, to fake a different height level. This can be useful for top-down view games. + + + + + + Emitted when this [TileMapLayer]'s properties changes. This includes modified cells, properties, or changes made to its assigned [TileSet]. + [b]Note:[/b] This signal may be emitted very often when batch-modifying a [TileMapLayer]. Avoid executing complex processing in a connected function, and consider delaying it to the end of the frame instead (i.e. calling [method Object.call_deferred]). + + + + + + Hide the collisions or navigation debug shapes in the editor, and use the debug settings to determine their visibility in game (i.e. [member SceneTree.debug_collisions_hint] or [member SceneTree.debug_navigation_hint]). + + + Always hide the collisions or navigation debug shapes. + + + Always show the collisions or navigation debug shapes. + + + diff --git a/modules/layered_tile_map/doc_classes/LayeredTileMapLayerGroup.xml b/modules/layered_tile_map/doc_classes/LayeredTileMapLayerGroup.xml deleted file mode 100644 index f61e624f1..000000000 --- a/modules/layered_tile_map/doc_classes/LayeredTileMapLayerGroup.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - Groups a set of tile map layers together, allowing them to share a provided [LayeredTileSet]. - - - Groups together tile map layers as part or the same map, replacing the [LayeredTileMap] node. Child layers will use this node's [member tile_set]. - The editor also uses [LayeredTileMapLayerGroup] as a way to store which layers are selected in a given group. This allows highlighting the currently selected layers. - - - - - - - - The assigned [LayeredTileSet]. This LayeredTileSet will be applied to all child layers. - - - - - diff --git a/modules/layered_tile_map/editor/layered_tile_map_layer_editor.cpp b/modules/layered_tile_map/editor/layered_tile_map_layer_editor.cpp index 4f8e72433..2014654ee 100644 --- a/modules/layered_tile_map/editor/layered_tile_map_layer_editor.cpp +++ b/modules/layered_tile_map/editor/layered_tile_map_layer_editor.cpp @@ -38,6 +38,7 @@ #include "editor/editor_scale.h" #include "editor/editor_settings.h" #include "editor/plugins/canvas_item_editor_plugin.h" +#include "editor/multi_node_edit.h" #include "core/object/undo_redo.h" @@ -116,7 +117,7 @@ void LayeredTileMapLayerEditorTilesPlugin::_update_transform_buttons() { return; } - Ref tile_set = edited_layer->get_effective_tile_set(); + Ref tile_set = edited_layer->get_tile_set(); if (tile_set.is_null() || selection_pattern.is_null()) { return; } @@ -190,7 +191,7 @@ void LayeredTileMapLayerEditorTilesPlugin::_update_tile_set_sources_list() { return; } - Ref tile_set = edited_layer->get_effective_tile_set(); + Ref tile_set = edited_layer->get_tile_set(); if (tile_set.is_null()) { return; } @@ -278,7 +279,7 @@ void LayeredTileMapLayerEditorTilesPlugin::_update_source_display() { return; } - Ref tile_set = edited_layer->get_effective_tile_set(); + Ref tile_set = edited_layer->get_tile_set(); if (tile_set.is_null()) { return; } @@ -324,7 +325,7 @@ void LayeredTileMapLayerEditorTilesPlugin::_patterns_item_list_gui_input(const R return; } - Ref tile_set = edited_layer->get_effective_tile_set(); + Ref tile_set = edited_layer->get_tile_set(); if (tile_set.is_null()) { return; } @@ -370,7 +371,7 @@ void LayeredTileMapLayerEditorTilesPlugin::_update_patterns_list() { return; } - Ref tile_set = edited_layer->get_effective_tile_set(); + Ref tile_set = edited_layer->get_tile_set(); if (tile_set.is_null()) { return; } @@ -409,7 +410,7 @@ void LayeredTileMapLayerEditorTilesPlugin::_update_atlas_view() { return; } - Ref tile_set = edited_layer->get_effective_tile_set(); + Ref tile_set = edited_layer->get_tile_set(); if (tile_set.is_null()) { return; } @@ -430,7 +431,7 @@ void LayeredTileMapLayerEditorTilesPlugin::_update_scenes_collection_view() { return; } - Ref tile_set = edited_layer->get_effective_tile_set(); + Ref tile_set = edited_layer->get_tile_set(); if (tile_set.is_null()) { return; } @@ -491,7 +492,7 @@ void LayeredTileMapLayerEditorTilesPlugin::_scenes_list_multi_selected(int p_ind return; } - Ref tile_set = edited_layer->get_effective_tile_set(); + Ref tile_set = edited_layer->get_tile_set(); if (tile_set.is_null()) { return; } @@ -566,7 +567,7 @@ bool LayeredTileMapLayerEditorTilesPlugin::forward_canvas_gui_input(const Ref tile_set = edited_layer->get_effective_tile_set(); + Ref tile_set = edited_layer->get_tile_set(); if (tile_set.is_null()) { return false; } @@ -826,7 +827,7 @@ void LayeredTileMapLayerEditorTilesPlugin::forward_canvas_draw_over_viewport(Con return; } - Ref tile_set = edited_layer->get_effective_tile_set(); + Ref tile_set = edited_layer->get_tile_set(); if (tile_set.is_null()) { return; } @@ -1066,7 +1067,7 @@ LayeredTileMapCell LayeredTileMapLayerEditorTilesPlugin::_pick_random_tile(Ref tile_set = edited_layer->get_effective_tile_set(); + Ref tile_set = edited_layer->get_tile_set(); if (tile_set.is_null()) { return LayeredTileMapCell(); } @@ -1118,7 +1119,7 @@ HashMap LayeredTileMapLayerEditorTilesPlugin::_dra return HashMap(); } - Ref tile_set = edited_layer->get_effective_tile_set(); + Ref tile_set = edited_layer->get_tile_set(); if (tile_set.is_null()) { return HashMap(); } @@ -1171,7 +1172,7 @@ HashMap LayeredTileMapLayerEditorTilesPlugin::_dra return HashMap(); } - Ref tile_set = edited_layer->get_effective_tile_set(); + Ref tile_set = edited_layer->get_tile_set(); if (tile_set.is_null()) { return HashMap(); } @@ -1230,7 +1231,7 @@ HashMap LayeredTileMapLayerEditorTilesPlugin::_dra return HashMap(); } - Ref tile_set = edited_layer->get_effective_tile_set(); + Ref tile_set = edited_layer->get_tile_set(); if (tile_set.is_null()) { return HashMap(); } @@ -1352,7 +1353,7 @@ void LayeredTileMapLayerEditorTilesPlugin::_stop_dragging() { return; } - Ref tile_set = edited_layer->get_effective_tile_set(); + Ref tile_set = edited_layer->get_tile_set(); if (tile_set.is_null()) { return; } @@ -1661,7 +1662,7 @@ void LayeredTileMapLayerEditorTilesPlugin::_update_fix_selected_and_hovered() { selection_pattern.instance(); return; } - Ref tile_set = edited_layer->get_effective_tile_set(); + Ref tile_set = edited_layer->get_tile_set(); if (tile_set.is_null()) { hovered_tile.source_id = LayeredTileSet::INVALID_SOURCE; hovered_tile.set_atlas_coords(LayeredTileSetSource::INVALID_ATLAS_COORDS); @@ -1751,7 +1752,7 @@ void LayeredTileMapLayerEditorTilesPlugin::_update_selection_pattern_from_tilema return; } - Ref tile_set = edited_layer->get_effective_tile_set(); + Ref tile_set = edited_layer->get_tile_set(); if (tile_set.is_null()) { return; } @@ -1772,7 +1773,7 @@ void LayeredTileMapLayerEditorTilesPlugin::_update_selection_pattern_from_tilese return; } - Ref tile_set = edited_layer->get_effective_tile_set(); + Ref tile_set = edited_layer->get_tile_set(); if (tile_set.is_null()) { return; } @@ -1851,7 +1852,7 @@ void LayeredTileMapLayerEditorTilesPlugin::_update_selection_pattern_from_tilese return; } - Ref tile_set = edited_layer->get_effective_tile_set(); + Ref tile_set = edited_layer->get_tile_set(); if (tile_set.is_null()) { return; } @@ -1890,7 +1891,7 @@ void LayeredTileMapLayerEditorTilesPlugin::_tile_atlas_control_draw() { return; } - Ref tile_set = edited_layer->get_effective_tile_set(); + Ref tile_set = edited_layer->get_tile_set(); if (tile_set.is_null()) { return; } @@ -1970,7 +1971,7 @@ void LayeredTileMapLayerEditorTilesPlugin::_tile_atlas_control_gui_input(const R return; } - Ref tile_set = edited_layer->get_effective_tile_set(); + Ref tile_set = edited_layer->get_tile_set(); if (tile_set.is_null()) { return; } @@ -2076,7 +2077,7 @@ void LayeredTileMapLayerEditorTilesPlugin::_tile_alternatives_control_draw() { return; } - Ref tile_set = edited_layer->get_effective_tile_set(); + Ref tile_set = edited_layer->get_tile_set(); if (tile_set.is_null()) { return; } @@ -2128,7 +2129,7 @@ void LayeredTileMapLayerEditorTilesPlugin::_tile_alternatives_control_gui_input( return; } - Ref tile_set = edited_layer->get_effective_tile_set(); + Ref tile_set = edited_layer->get_tile_set(); if (tile_set.is_null()) { return; } @@ -2266,17 +2267,23 @@ void LayeredTileMapLayerEditorTilesPlugin::edit(ObjectID p_tile_map_layer_id) { // Disable sort button if the tileset is read-only LayeredTileMapLayer *edited_layer = _get_edited_layer(); + Ref tile_set; if (edited_layer) { - Ref tile_set = edited_layer->get_effective_tile_set(); + tile_set = edited_layer->get_tile_set(); + if (tile_set.is_valid()) { //source_sort_button->set_disabled(EditorNode::get_singleton()->is_resource_read_only(tile_set)); source_sort_button->set_disabled(false); } } - if (edited_tile_map_layer_id != p_tile_map_layer_id) { - edited_tile_map_layer_id = p_tile_map_layer_id; + LayeredTileMapLayer *new_tile_map_layer = Object::cast_to(ObjectDB::get_instance(edited_tile_map_layer_id)); + Ref new_tile_set; + if (new_tile_map_layer) { + new_tile_set = new_tile_map_layer->get_tile_set(); + } + if (tile_set.is_valid() && tile_set != new_tile_set) { // Clear the selection. tile_set_selection.clear(); patterns_item_list->unselect_all(); @@ -2638,7 +2645,7 @@ HashMap LayeredTileMapLayerEditorTerrainsPlugin::_ return HashMap(); } - Ref tile_set = edited_layer->get_effective_tile_set(); + Ref tile_set = edited_layer->get_tile_set(); if (tile_set.is_null()) { return HashMap(); } @@ -2691,7 +2698,7 @@ HashMap LayeredTileMapLayerEditorTerrainsPlugin::_ return HashMap(); } - Ref tile_set = edited_layer->get_effective_tile_set(); + Ref tile_set = edited_layer->get_tile_set(); if (tile_set.is_null()) { return HashMap(); } @@ -2738,7 +2745,7 @@ HashMap LayeredTileMapLayerEditorTerrainsPlugin::_ return HashMap(); } - Ref tile_set = edited_layer->get_effective_tile_set(); + Ref tile_set = edited_layer->get_tile_set(); if (tile_set.is_null()) { return HashMap(); } @@ -2762,7 +2769,7 @@ HashMap LayeredTileMapLayerEditorTerrainsPlugin::_ return HashMap(); } - Ref tile_set = edited_layer->get_effective_tile_set(); + Ref tile_set = edited_layer->get_tile_set(); if (tile_set.is_null()) { return HashMap(); } @@ -2796,7 +2803,7 @@ RBSet LayeredTileMapLayerEditorTerrainsPlugin::_get_cells_for_bucket_f return RBSet(); } - Ref tile_set = edited_layer->get_effective_tile_set(); + Ref tile_set = edited_layer->get_tile_set(); if (tile_set.is_null()) { return RBSet(); } @@ -2908,7 +2915,7 @@ HashMap LayeredTileMapLayerEditorTerrainsPlugin::_ return HashMap(); } - const Ref &tile_set = edited_layer->get_effective_tile_set(); + const Ref &tile_set = edited_layer->get_tile_set(); if (tile_set.is_null()) { return HashMap(); } @@ -2937,7 +2944,7 @@ void LayeredTileMapLayerEditorTerrainsPlugin::_stop_dragging() { return; } - const Ref &tile_set = edited_layer->get_effective_tile_set(); + const Ref &tile_set = edited_layer->get_tile_set(); if (tile_set.is_null()) { return; } @@ -3071,7 +3078,7 @@ void LayeredTileMapLayerEditorTerrainsPlugin::_update_selection() { return; } - Ref tile_set = edited_layer->get_effective_tile_set(); + Ref tile_set = edited_layer->get_tile_set(); if (tile_set.is_null()) { return; } @@ -3123,7 +3130,7 @@ bool LayeredTileMapLayerEditorTerrainsPlugin::forward_canvas_gui_input(const Ref return false; } - Ref tile_set = edited_layer->get_effective_tile_set(); + Ref tile_set = edited_layer->get_tile_set(); if (tile_set.is_null()) { return false; } @@ -3262,7 +3269,7 @@ void LayeredTileMapLayerEditorTerrainsPlugin::forward_canvas_draw_over_viewport( return; } - Ref tile_set = edited_layer->get_effective_tile_set(); + Ref tile_set = edited_layer->get_tile_set(); if (tile_set.is_null()) { return; } @@ -3388,7 +3395,7 @@ void LayeredTileMapLayerEditorTerrainsPlugin::_update_terrains_cache() { return; } - Ref tile_set = edited_layer->get_effective_tile_set(); + Ref tile_set = edited_layer->get_tile_set(); if (tile_set.is_null()) { return; } @@ -3458,7 +3465,7 @@ void LayeredTileMapLayerEditorTerrainsPlugin::_update_terrains_tree() { const LayeredTileMapLayer *edited_layer = _get_edited_layer(); ERR_FAIL_NULL(edited_layer); - Ref tile_set = edited_layer->get_effective_tile_set(); + Ref tile_set = edited_layer->get_tile_set(); if (tile_set.is_null()) { return; } @@ -3505,7 +3512,7 @@ void LayeredTileMapLayerEditorTerrainsPlugin::_update_tiles_list() { return; } - Ref tile_set = edited_layer->get_effective_tile_set(); + Ref tile_set = edited_layer->get_tile_set(); if (tile_set.is_null()) { return; } @@ -3753,12 +3760,59 @@ LayeredTileMapLayer *LayeredTileMapLayerEditor::_get_edited_layer() const { return Object::cast_to(ObjectDB::get_instance(edited_tile_map_layer_id)); } +void LayeredTileMapLayerEditor::_find_tile_map_layers_in_scene(Node *p_current, const Node *p_owner, Vector &r_list) const { + ERR_FAIL_COND(!p_current || !p_owner); + if (p_current != p_owner && p_current->get_owner() != p_owner) { + return; + } + LayeredTileMapLayer *layer = Object::cast_to(p_current); + if (layer) { + r_list.push_back(layer); + } + for (int i = 0; i < p_current->get_child_count(); i++) { + Node *child = p_current->get_child(i); + _find_tile_map_layers_in_scene(child, p_owner, r_list); + } +} + +void LayeredTileMapLayerEditor::_update_tile_map_layers_in_scene_list_cache() { + if (!layers_in_scene_list_cache_needs_update) { + return; + } + EditorNode *en = EditorNode::get_singleton(); + Node *edited_scene_root = en->get_edited_scene(); + if (!edited_scene_root) { + return; + } + + tile_map_layers_in_scene_cache.clear(); + _find_tile_map_layers_in_scene(edited_scene_root, edited_scene_root, tile_map_layers_in_scene_cache); + layers_in_scene_list_cache_needs_update = false; +} + +void LayeredTileMapLayerEditor::_node_change(Node *p_node) { + if (!layers_in_scene_list_cache_needs_update && Engine::get_singleton()->is_editor_hint() && + is_inside_tree() && get_tree()->get_edited_scene_root() && + get_tree()->get_edited_scene_root()->get_parent()->is_a_parent_of(this) && + Object::cast_to(p_node)) { + layers_in_scene_list_cache_needs_update = true; + } +} + void LayeredTileMapLayerEditor::_notification(int p_what) { switch (p_what) { + case NOTIFICATION_READY: { + get_tree()->connect("node_added", this, "_node_change"); + get_tree()->connect("node_removed", this, "_node_change"); + } break; + case NOTIFICATION_THEME_CHANGED: { missing_tile_texture = get_theme_icon("StatusWarning", "EditorIcons"); warning_pattern_texture = get_theme_icon("WarningPattern", "EditorIcons"); advanced_menu_button->set_icon(get_theme_icon("Tools", "EditorIcons")); + select_previous_layer->set_icon(get_theme_icon("MoveUp", "EditorIcons")); + select_next_layer->set_icon(get_theme_icon("MoveDown", "EditorIcons")); + select_all_layers->set_icon(get_theme_icon("FileList", "EditorIcons")); toggle_grid_button->set_icon(get_theme_icon("Grid", "EditorIcons")); toggle_grid_button->set_pressed(EDITOR_GET("editors/layered_tiles_editor/display_grid")); toggle_highlight_selected_layer_button->set_icon(get_theme_icon("LayeredTileMapHighlightSelected", "EditorIcons")); @@ -3766,18 +3820,18 @@ void LayeredTileMapLayerEditor::_notification(int p_what) { } break; case NOTIFICATION_INTERNAL_PROCESS: { - if (is_visible_in_tree() && tileset_changed_needs_update) { + if (is_visible_in_tree() && tile_map_layer_changed_needs_update) { _update_bottom_panel(); - update_layers_selector(); - _update_highlighting_toggle(); + _update_layers_selector(); tabs_plugins[tabs_bar->get_current_tab()]->tile_set_changed(); CanvasItemEditor::get_singleton()->update_viewport(); - tileset_changed_needs_update = false; + tile_map_layer_changed_needs_update = false; } } break; case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: { toggle_grid_button->set_pressed_no_signal(EDITOR_GET("editors/layered_tiles_editor/display_grid")); + toggle_highlight_selected_layer_button->set_pressed_no_signal(EDITOR_GET("editors/layered_tiles_editor/highlight_selected_layer")); } break; } } @@ -3785,8 +3839,6 @@ void LayeredTileMapLayerEditor::_notification(int p_what) { void LayeredTileMapLayerEditor::_bind_methods() { ADD_SIGNAL(MethodInfo("change_selected_layer_request", PropertyInfo(Variant::STRING_NAME, "layer_name"))); - ClassDB::bind_method(D_METHOD("update_layers_selector"), &LayeredTileMapLayerEditor::update_layers_selector); - ClassDB::bind_method(D_METHOD("_tile_map_layer_changed"), &LayeredTileMapLayerEditor::_tile_map_layer_changed); ClassDB::bind_method(D_METHOD("_tab_changed"), &LayeredTileMapLayerEditor::_tab_changed); ClassDB::bind_method(D_METHOD("_layers_selection_item_selected"), &LayeredTileMapLayerEditor::_layers_selection_item_selected); @@ -3794,6 +3846,12 @@ void LayeredTileMapLayerEditor::_bind_methods() { ClassDB::bind_method(D_METHOD("_on_grid_toggled"), &LayeredTileMapLayerEditor::_on_grid_toggled); ClassDB::bind_method(D_METHOD("_advanced_menu_button_id_pressed"), &LayeredTileMapLayerEditor::_advanced_menu_button_id_pressed); ClassDB::bind_method(D_METHOD("_move_tile_map_array_element"), &LayeredTileMapLayerEditor::_move_tile_map_array_element); + + ClassDB::bind_method(D_METHOD("_node_change"), &LayeredTileMapLayerEditor::_node_change); + + ClassDB::bind_method(D_METHOD("_select_previous_layer_pressed"), &LayeredTileMapLayerEditor::_select_previous_layer_pressed); + ClassDB::bind_method(D_METHOD("_select_next_layer_pressed"), &LayeredTileMapLayerEditor::_select_next_layer_pressed); + ClassDB::bind_method(D_METHOD("_select_all_layers_pressed"), &LayeredTileMapLayerEditor::_select_all_layers_pressed); } void LayeredTileMapLayerEditor::_on_grid_toggled(bool p_pressed) { @@ -3804,18 +3862,187 @@ void LayeredTileMapLayerEditor::_on_grid_toggled(bool p_pressed) { } } +void LayeredTileMapLayerEditor::_select_previous_layer_pressed() { + _layers_select_next_or_previous(false); +} + +void LayeredTileMapLayerEditor::_select_next_layer_pressed() { + _layers_select_next_or_previous(true); +} + +void LayeredTileMapLayerEditor::_select_all_layers_pressed() { + EditorNode *en = EditorNode::get_singleton(); + Node *edited_scene_root = en->get_edited_scene(); + ERR_FAIL_NULL(edited_scene_root); + + en->get_editor_selection()->clear(); + if (tile_map_layers_in_scene_cache.size() == 1) { + en->edit_node(tile_map_layers_in_scene_cache[0]); + en->get_editor_selection()->add_node(tile_map_layers_in_scene_cache[0]); + } else { + _update_tile_map_layers_in_scene_list_cache(); + Ref multi_node_edit = memnew(MultiNodeEdit); + for (int i = 0; i < tile_map_layers_in_scene_cache.size(); ++i) { + LayeredTileMapLayer *layer = tile_map_layers_in_scene_cache[i]; + multi_node_edit->add_node(edited_scene_root->get_path_to(layer)); + en->get_editor_selection()->add_node(layer); + } + en->push_item(multi_node_edit.ptr()); + } +} + void LayeredTileMapLayerEditor::_layers_selection_item_selected(int p_index) { - emit_signal("change_selected_layer_request", layers_selection_button->get_item_metadata(p_index)); + LayeredTileMapLayer *edited_layer = _get_edited_layer(); + ERR_FAIL_NULL(edited_layer); + + LayeredTileMap *tile_map = Object::cast_to(edited_layer->get_parent()); + ERR_FAIL_NULL(tile_map); + + LayeredTileMapLayer *new_edited = Object::cast_to(tile_map->get_child(p_index)); + edit(new_edited); +} + +void LayeredTileMapLayerEditor::_update_layers_selector() { + const LayeredTileMapLayer *edited_layer = _get_edited_layer(); + + // Update the selector. + layers_selection_button->clear(); + layers_selection_button->hide(); + select_all_layers->show(); + select_next_layer->set_disabled(false); + select_previous_layer->set_disabled(false); + advanced_menu_button->get_popup()->set_item_disabled(ADVANCED_MENU_EXTRACT_TILE_MAP_LAYERS, true); + if (edited_layer) { + LayeredTileMap *tile_map = Object::cast_to(edited_layer->get_parent()); + if (tile_map && edited_layer->get_index_in_tile_map() >= 0) { + // Build the list of layers. + for (int i = 0; i < tile_map->get_layers_count(); i++) { + const LayeredTileMapLayer *layer = Object::cast_to(tile_map->get_child(i)); + if (layer) { + int index = layers_selection_button->get_item_count(); + layers_selection_button->add_item(layer->get_name()); + layers_selection_button->set_item_metadata(index, layer->get_name()); + if (edited_layer == layer) { + layers_selection_button->select(index); + } + } + } + + // Disable selector if there's no layer to select. + layers_selection_button->set_disabled(false); + if (layers_selection_button->get_item_count() == 0) { + layers_selection_button->set_disabled(true); + layers_selection_button->set_text(TTR("No Layers")); + } + + // Disable next/previous if there's one or less layers. + if (layers_selection_button->get_item_count() <= 1) { + select_next_layer->set_disabled(true); + select_previous_layer->set_disabled(true); + } + layers_selection_button->show(); + select_all_layers->hide(); + + // Enable the "extract as TileMapLayer" option only if we are editing a TleMap. + advanced_menu_button->get_popup()->set_item_disabled(ADVANCED_MENU_EXTRACT_TILE_MAP_LAYERS, false); + } + } else { + select_all_layers->hide(); + select_next_layer->set_disabled(true); + select_previous_layer->set_disabled(true); + } +} + +void LayeredTileMapLayerEditor::_clear_all_layers_highlighting() { + // Note: This function might be removed if we remove the TileMap node at some point. + // All processing could be done in _update_all_layers_highlighting otherwise. + LayeredTileMapLayer *edited_layer = _get_edited_layer(); + + // Use default mode. + if (edited_layer && edited_layer->get_index_in_tile_map() >= 0) { + // For the TileMap node. + LayeredTileMap *tile_map = Object::cast_to(edited_layer->get_parent()); + if (tile_map) { + for (int i = 0; i < tile_map->get_layers_count(); i++) { + LayeredTileMapLayer *layer = Object::cast_to(tile_map->get_child(i)); + layer->set_highlight_mode(LayeredTileMapLayer::HIGHLIGHT_MODE_DEFAULT); + } + } + } else { + // For other TileMapLayer nodes. + _update_tile_map_layers_in_scene_list_cache(); + for (int i = 0; i < tile_map_layers_in_scene_cache.size(); ++i) { + LayeredTileMapLayer *layer = tile_map_layers_in_scene_cache[i]; + + layer->set_highlight_mode(LayeredTileMapLayer::HIGHLIGHT_MODE_DEFAULT); + } + } +} + +void LayeredTileMapLayerEditor::_update_all_layers_highlighting() { + EditorNode *en = EditorNode::get_singleton(); + Node *edited_scene_root = en->get_edited_scene(); + if (!edited_scene_root) { + return; + } + + // Get selected layer. + LayeredTileMapLayer *edited_layer = _get_edited_layer(); + + bool highlight_selected_layer = EDITOR_GET("editors/layered_tiles_editor/highlight_selected_layer"); + if (edited_layer && highlight_selected_layer) { + int edited_z_index = edited_layer->get_z_index(); + + if (edited_layer->get_index_in_tile_map() >= 0) { + // For the TileMap node. + LayeredTileMap *tile_map = Object::cast_to(edited_layer->get_parent()); + ERR_FAIL_NULL(tile_map); + + bool passed = false; + for (int i = 0; i < tile_map->get_layers_count(); i++) { + LayeredTileMapLayer *layer = Object::cast_to(tile_map->get_child(i)); + if (layer == edited_layer) { + passed = true; + layer->set_highlight_mode(LayeredTileMapLayer::HIGHLIGHT_MODE_DEFAULT); + } else { + if (passed || layer->get_z_index() > edited_z_index) { + layer->set_highlight_mode(LayeredTileMapLayer::HIGHLIGHT_MODE_ABOVE); + } else { + layer->set_highlight_mode(LayeredTileMapLayer::HIGHLIGHT_MODE_BELOW); + } + } + } + } else { + // Update highlight mode for independent layers. + _update_tile_map_layers_in_scene_list_cache(); + bool passed = false; + for (int i = 0; i < tile_map_layers_in_scene_cache.size(); ++i) { + LayeredTileMapLayer *layer = tile_map_layers_in_scene_cache[i]; + + if (layer == edited_layer) { + passed = true; + layer->set_highlight_mode(LayeredTileMapLayer::HIGHLIGHT_MODE_DEFAULT); + } else { + if (passed || layer->get_z_index() > edited_z_index) { + layer->set_highlight_mode(LayeredTileMapLayer::HIGHLIGHT_MODE_ABOVE); + } else { + layer->set_highlight_mode(LayeredTileMapLayer::HIGHLIGHT_MODE_BELOW); + } + } + } + } + } } void LayeredTileMapLayerEditor::_highlight_selected_layer_button_toggled(bool p_pressed) { LayeredTileMapLayer *edited_layer = _get_edited_layer(); - ERR_FAIL_NULL(edited_layer); + + if (!edited_layer) { + return; + } - LayeredTileMapLayerGroup *tile_map_layer_group = Object::cast_to(edited_layer->get_parent()); - ERR_FAIL_NULL(tile_map_layer_group); - - tile_map_layer_group->set_highlight_selected_layer(p_pressed); + EditorSettings::get_singleton()->set("editors/layered_tiles_editor/highlight_selected_layer", p_pressed); + _update_all_layers_highlighting(); } void LayeredTileMapLayerEditor::_advanced_menu_button_id_pressed(int p_id) { @@ -3824,12 +4051,12 @@ void LayeredTileMapLayerEditor::_advanced_menu_button_id_pressed(int p_id) { return; } - Ref tile_set = edited_layer->get_effective_tile_set(); + Ref tile_set = edited_layer->get_tile_set(); if (tile_set.is_null()) { return; } - if (p_id == 0) { // Replace Tile Proxies + if (p_id == ADVANCED_MENU_REPLACE_WITH_PROXIES) { // Replace Tile Proxies UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo(); undo_redo->create_action(TTR("Replace Tiles with Proxies")); PoolVector2iArray used_cells = edited_layer->get_used_cells(); @@ -3847,36 +4074,81 @@ void LayeredTileMapLayerEditor::_advanced_menu_button_id_pressed(int p_id) { } } + undo_redo->commit_action(); + } else if (p_id == ADVANCED_MENU_EXTRACT_TILE_MAP_LAYERS) { // Transform internal TileMap layers into TileMapLayers. + ERR_FAIL_COND(edited_layer->get_index_in_tile_map() < 0); + + EditorNode *en = EditorNode::get_singleton(); + Node *edited_scene_root = en->get_edited_scene(); + ERR_FAIL_NULL(edited_scene_root); + + UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo(); + undo_redo->create_action(TTR("Extract TileMap layers as individual TileMapLayer nodes")); + + LayeredTileMap *tile_map = Object::cast_to(edited_layer->get_parent()); + for (int i = 0; i < tile_map->get_layers_count(); i++) { + undo_redo->add_do_method(tile_map, "remove_layer", 0); + } + + for (int i = 0; i < tile_map->get_layers_count(); i++) { + LayeredTileMapLayer *new_layer = tile_map->duplicate_layer_from_internal(i); + undo_redo->add_do_method(tile_map, "add_child", new_layer); + undo_redo->add_do_method(new_layer, "set_owner", edited_scene_root); + undo_redo->add_do_property(new_layer, "tile_set", tile_map->get_tileset()); // Workaround for a bug: #89947. + undo_redo->add_undo_method(tile_map, "remove_child", new_layer); + undo_redo->add_do_reference(new_layer); + } + + List prop_list; + tile_map->get_property_list(&prop_list); + + for (List::Element *E = prop_list.front(); E; E = E->next()) { + undo_redo->add_undo_property(tile_map, E->get().name, tile_map->get(E->get().name)); + } + undo_redo->commit_action(); } } void LayeredTileMapLayerEditor::_update_bottom_panel() { const LayeredTileMapLayer *edited_layer = _get_edited_layer(); - if (!edited_layer) { - return; + + Ref tile_set; + if (edited_layer) { + tile_set = edited_layer->get_tile_set(); } - Ref tile_set = edited_layer->get_effective_tile_set(); - - // Update the visibility of controls. - missing_tileset_label->set_visible(tile_set.is_null()); - + // Update state labels. + if (is_multi_node_edit) { + cant_edit_label->set_text(TTR("Can't edit multiple layers at once.")); + cant_edit_label->show(); + } else if (!edited_layer) { + cant_edit_label->set_text(TTR("The selected TileMap has no layer to edit.")); + cant_edit_label->show(); + } else if (!edited_layer->is_enabled() || !edited_layer->is_visible_in_tree()) { + cant_edit_label->set_text(TTR("The edited layer is disabled or invisible")); + cant_edit_label->show(); + } else if (tile_set.is_null()) { + cant_edit_label->set_text(TTR("The edited TileMap or TileMapLayer node has no TileSet resource.\nCreate or load a TileSet resource in the Tile Set property in the inspector.")); + cant_edit_label->show(); + } else { + cant_edit_label->hide(); + } + + // Update tabs visibility. for (uint32_t i = 0; i < tabs_data.size(); ++i) { LayeredTileMapLayerSubEditorPlugin::TabData &tab_data = tabs_data[i]; tab_data.panel->hide(); } - if (tile_set.is_valid()) { - tabs_data[tabs_bar->get_current_tab()].panel->show(); - } + tabs_data[tabs_bar->get_current_tab()].panel->set_visible(!cant_edit_label->is_visible()); } Vector LayeredTileMapLayerEditor::get_line(const LayeredTileMapLayer *p_tile_map_layer, Vector2i p_from_cell, Vector2i p_to_cell) { ERR_FAIL_NULL_V(p_tile_map_layer, Vector()); - Ref tile_set = p_tile_map_layer->get_effective_tile_set(); + Ref tile_set = p_tile_map_layer->get_tile_set(); ERR_FAIL_COND_V(tile_set.is_null(), Vector()); if (tile_set->get_tile_shape() == LayeredTileSet::TILE_SHAPE_SQUARE) { @@ -3947,7 +4219,7 @@ Vector LayeredTileMapLayerEditor::get_line(const LayeredTileMapLayer * } void LayeredTileMapLayerEditor::_tile_map_layer_changed() { - tileset_changed_needs_update = true; + tile_map_layer_changed_needs_update = true; } void LayeredTileMapLayerEditor::_tab_changed(int p_tab_id) { @@ -3971,7 +4243,7 @@ void LayeredTileMapLayerEditor::_tab_changed(int p_tab_id) { LayeredTileMapLayer *tile_map_layer = _get_edited_layer(); if (tile_map_layer) { - if (tile_map_layer->get_effective_tile_set().is_valid()) { + if (tile_map_layer->get_tile_set().is_valid()) { tabs_data[tabs_bar->get_current_tab()].panel->show(); } } @@ -3990,36 +4262,39 @@ void LayeredTileMapLayerEditor::_layers_select_next_or_previous(bool p_next) { return; } - LayeredTileMapLayerGroup *tile_map_layer_group = Object::cast_to(edited_layer->get_parent()); - if (!tile_map_layer_group) { - return; - } + EditorNode *en = EditorNode::get_singleton(); + Node *edited_scene_root = en->get_edited_scene(); + ERR_FAIL_NULL(edited_scene_root); + + LayeredTileMapLayer *new_selected_layer = nullptr; int inc = p_next ? 1 : -1; - int index = Math::posmod(edited_layer->get_index() + inc, tile_map_layer_group->get_child_count()); - const LayeredTileMapLayer *new_selected_layer = Object::cast_to(tile_map_layer_group->get_child(index)); - while (new_selected_layer != edited_layer) { - if (new_selected_layer && new_selected_layer->is_enabled()) { - break; + if (edited_layer->get_index_in_tile_map() >= 0) { + // Part of a TileMap. + LayeredTileMap *tile_map = Object::cast_to(edited_layer->get_parent()); + new_selected_layer = Object::cast_to(tile_map->get_child(Math::posmod(edited_layer->get_index_in_tile_map() + inc, tile_map->get_layers_count()))); + } else { + // Individual layer. + _update_tile_map_layers_in_scene_list_cache(); + int edited_index = -1; + for (int i = 0; i < tile_map_layers_in_scene_cache.size(); i++) { + if (tile_map_layers_in_scene_cache[i] == edited_layer) { + edited_index = i; + break; + } } - index = Math::posmod((index + inc), tile_map_layer_group->get_child_count()); - new_selected_layer = Object::cast_to(tile_map_layer_group->get_child(index)); + new_selected_layer = tile_map_layers_in_scene_cache[Math::posmod(edited_index + inc, tile_map_layers_in_scene_cache.size())]; } - if (new_selected_layer != edited_layer) { - emit_signal("change_selected_layer_request", new_selected_layer->get_name()); - } -} + ERR_FAIL_NULL(new_selected_layer); -void LayeredTileMapLayerEditor::_update_highlighting_toggle() { - const LayeredTileMapLayer *edited_layer = _get_edited_layer(); - if (!edited_layer) { - return; - } - - LayeredTileMapLayerGroup *tile_map_layer_group = Object::cast_to(edited_layer->get_parent()); - if (tile_map_layer_group) { - toggle_highlight_selected_layer_button->set_pressed(tile_map_layer_group->is_highlighting_selected_layer()); + if (edited_layer->get_index_in_tile_map() < 0) { + // Only if not part of a TileMap. + en->edit_node(new_selected_layer); + en->get_editor_selection()->clear(); + en->get_editor_selection()->add_node(new_selected_layer); + } else { + edit(new_selected_layer); } } @@ -4118,7 +4393,7 @@ void LayeredTileMapLayerEditor::forward_canvas_draw_over_viewport(Control *p_ove return; } - Ref tile_set = edited_layer->get_effective_tile_set(); + Ref tile_set = edited_layer->get_tile_set(); if (tile_set.is_null()) { return; } @@ -4146,29 +4421,25 @@ void LayeredTileMapLayerEditor::forward_canvas_draw_over_viewport(Control *p_ove } 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 a = tile_set->map_tile_proxy(tile_source_id, tile_atlas_coords, tile_alternative_tile); - if (int(a[0]) == tile_source_id && Vector2i(a[1]) == tile_atlas_coords && int(a[2]) == tile_alternative_tile) { - // Only display the pattern if we have no proxy tile. - 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(); + // Generate a random color from the hashed identifier 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); + 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. - Transform2D tile_xform; - tile_xform.set_origin(tile_set->map_to_local(coords)); - tile_xform.set_scale(tile_shape_size); - tile_set->draw_tile_shape(p_overlay, xform * tile_xform, color, true, warning_pattern_texture); - } + // Display the warning pattern. + Transform2D tile_xform; + tile_xform.set_origin(tile_set->map_to_local(coords)); + tile_xform.set_scale(tile_shape_size); + tile_set->draw_tile_shape(p_overlay, xform * tile_xform, color, true, warning_pattern_texture); // Draw the warning icon. Vector2::Axis min_axis = (Vector2::Axis)missing_tile_texture->get_size().min_axis(); @@ -4244,75 +4515,51 @@ void LayeredTileMapLayerEditor::forward_canvas_draw_over_viewport(Control *p_ove tabs_plugins[tabs_bar->get_current_tab()]->forward_canvas_draw_over_viewport(p_overlay); } -void LayeredTileMapLayerEditor::edit(LayeredTileMapLayer *p_tile_map_layer) { - if (p_tile_map_layer && p_tile_map_layer->get_instance_id() == edited_tile_map_layer_id) { +void LayeredTileMapLayerEditor::edit(Object *p_edited) { + if (p_edited && p_edited->get_instance_id() == edited_tile_map_layer_id) { return; } + + _clear_all_layers_highlighting(); // Disconnect to changes. LayeredTileMapLayer *tile_map_layer = _get_edited_layer(); if (tile_map_layer) { tile_map_layer->disconnect("changed", this, "_tile_map_layer_changed"); + tile_map_layer->disconnect("visibility_changed", this, "_tile_map_layer_changed"); } // Update the edited layer. - if (p_tile_map_layer) { + LayeredTileMapLayer *new_layer = Object::cast_to(p_edited); + if (new_layer) { // Change the edited object. - edited_tile_map_layer_id = p_tile_map_layer->get_instance_id(); + edited_tile_map_layer_id = new_layer->get_instance_id(); tile_map_layer = _get_edited_layer(); // Connect to changes. if (!tile_map_layer->is_connected("changed", this, "_tile_map_layer_changed")) { tile_map_layer->connect("changed", this, "_tile_map_layer_changed"); + tile_map_layer->connect("visibility_changed", this, "_tile_map_layer_changed"); } } else { edited_tile_map_layer_id = ObjectID(); } - update_layers_selector(); - _update_highlighting_toggle(); + // Check if we are trying to use a MultiNodeEdit. + is_multi_node_edit = Object::cast_to(p_edited); - // Call the plugins. + // Call the plugins and update everything. tabs_plugins[tabs_bar->get_current_tab()]->edit(edited_tile_map_layer_id); + + _update_layers_selector(); + _update_all_layers_highlighting(); _tile_map_layer_changed(); } -void LayeredTileMapLayerEditor::update_layers_selector() { - const LayeredTileMapLayer *edited_layer = _get_edited_layer(); - if (!edited_layer) { - return; - } - - LayeredTileMapLayerGroup *tile_map_layer_group = Object::cast_to(edited_layer->get_parent()); - if (tile_map_layer_group) { - // Update the selector - layers_selection_button->show(); - layers_selection_button->clear(); - - // Build the list of layers. - for (int i = 0; i < tile_map_layer_group->get_child_count(); i++) { - const LayeredTileMapLayer *layer = Object::cast_to(tile_map_layer_group->get_child(i)); - if (layer) { - int index = layers_selection_button->get_item_count(); - layers_selection_button->add_item(layer->get_name()); - layers_selection_button->set_item_disabled(index, !layer->is_enabled()); - layers_selection_button->set_item_metadata(index, layer->get_name()); - if (edited_layer == layer) { - layers_selection_button->select(index); - } - } - } - - // Disable the button if there's no layer to select. - layers_selection_button->set_disabled(false); - if (layers_selection_button->get_item_count() == 0) { - layers_selection_button->set_disabled(true); - layers_selection_button->set_text(TTR("No Layers")); - } - } else { - layers_selection_button->hide(); - } +void LayeredTileMapLayerEditor::set_show_layer_selector(bool p_show_layer_selector) { + show_layers_selector = p_show_layer_selector; + _update_layers_selector(); } LayeredTileMapLayerEditor::LayeredTileMapLayerEditor() { @@ -4364,11 +4611,35 @@ LayeredTileMapLayerEditor::LayeredTileMapLayerEditor() { tile_map_toolbar->add_child(c); // Layer selector. + layer_selection_hbox = memnew(HBoxContainer); + tile_map_toolbar->add_child(layer_selection_hbox); + layers_selection_button = memnew(OptionButton); layers_selection_button->set_custom_minimum_size(Size2(200, 0)); layers_selection_button->set_tooltip(TTR("LayeredTileMap Layers")); layers_selection_button->connect("item_selected", this, "_layers_selection_item_selected"); - tile_map_toolbar->add_child(layers_selection_button); + layer_selection_hbox->add_child(layers_selection_button); + + select_previous_layer = memnew(Button); + select_previous_layer->set_theme_type_variation("FlatButton"); + select_previous_layer->set_tooltip(TTR("Select previous layer")); + select_previous_layer->connect("pressed", this, "_select_previous_layer_pressed"); + layer_selection_hbox->add_child(select_previous_layer); + + select_next_layer = memnew(Button); + select_next_layer->set_theme_type_variation("FlatButton"); + select_next_layer->set_tooltip(TTR("Select next layer")); + select_next_layer->connect("pressed", this, "_select_next_layer_pressed"); + layer_selection_hbox->add_child(select_next_layer); + + select_all_layers = memnew(Button); + select_all_layers->set_theme_type_variation("FlatButton"); + select_all_layers->set_text(TTR("Select all layers")); + select_all_layers->connect("pressed", this, "_select_all_layers_pressed"); + select_all_layers->set_tooltip(TTR("Select all TileMapLayers in scene")); + layer_selection_hbox->add_child(select_all_layers); + + // Highlighting selected layer. toggle_highlight_selected_layer_button = memnew(Button); toggle_highlight_selected_layer_button->set_theme_type_variation("FlatButton"); @@ -4392,18 +4663,19 @@ LayeredTileMapLayerEditor::LayeredTileMapLayerEditor() { advanced_menu_button = memnew(MenuButton); advanced_menu_button->set_flat(false); advanced_menu_button->set_theme_type_variation("FlatButton"); - advanced_menu_button->get_popup()->add_item(TTR("Automatically Replace Tiles with Proxies")); + advanced_menu_button->get_popup()->add_item(TTR("Automatically Replace Tiles with Proxies"), ADVANCED_MENU_REPLACE_WITH_PROXIES); + advanced_menu_button->get_popup()->add_item(TTR("Extract TileMap layers as individual TileMapLayer nodes"), ADVANCED_MENU_EXTRACT_TILE_MAP_LAYERS); advanced_menu_button->get_popup()->connect("id_pressed", this, "_advanced_menu_button_id_pressed"); tile_map_toolbar->add_child(advanced_menu_button); - missing_tileset_label = memnew(Label); - missing_tileset_label->set_text(TTR("The edited LayeredTileMap node has no LayeredTileSet resource.\nCreate or load a LayeredTileSet resource in the Tile Set property in the inspector.")); - 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); + // A label for editing errors. + cant_edit_label = memnew(Label); + cant_edit_label->set_h_size_flags(SIZE_EXPAND_FILL); + cant_edit_label->set_v_size_flags(SIZE_EXPAND_FILL); + cant_edit_label->set_align(Label::ALIGN_CENTER); + cant_edit_label->set_valign(Label::VALIGN_CENTER); + cant_edit_label->hide(); + add_child(cant_edit_label); for (unsigned int tab_index = 0; tab_index < tabs_data.size(); tab_index++) { add_child(tabs_data[tab_index].panel); diff --git a/modules/layered_tile_map/editor/layered_tile_map_layer_editor.h b/modules/layered_tile_map/editor/layered_tile_map_layer_editor.h index 370627759..88600ff8a 100644 --- a/modules/layered_tile_map/editor/layered_tile_map_layer_editor.h +++ b/modules/layered_tile_map/editor/layered_tile_map_layer_editor.h @@ -353,31 +353,54 @@ class LayeredTileMapLayerEditor : public VBoxContainer { GDCLASS(LayeredTileMapLayerEditor, VBoxContainer); private: - bool tileset_changed_needs_update = false; + bool tile_map_layer_changed_needs_update = false; ObjectID edited_tile_map_layer_id; + bool is_multi_node_edit = false; + Vector tile_map_layers_in_scene_cache; + bool layers_in_scene_list_cache_needs_update = false; LayeredTileMapLayer *_get_edited_layer() const; + void _find_tile_map_layers_in_scene(Node *p_current, const Node *p_owner, Vector &r_list) const; + void _update_tile_map_layers_in_scene_list_cache(); + void _node_change(Node *p_node); // Vector to keep plugins. Vector tile_map_editor_plugins; // Toolbar. HFlowContainer *tile_map_toolbar = nullptr; + + bool show_layers_selector = false; + HBoxContainer *layer_selection_hbox = nullptr; + Button *select_previous_layer = nullptr; + void _select_previous_layer_pressed(); + Button *select_next_layer = nullptr; + void _select_next_layer_pressed(); + Button *select_all_layers = nullptr; + void _select_all_layers_pressed(); OptionButton *layers_selection_button = nullptr; void _layers_selection_item_selected(int p_index); + void _update_layers_selector(); Button *toggle_highlight_selected_layer_button = nullptr; + void _clear_all_layers_highlighting(); + void _update_all_layers_highlighting(); void _highlight_selected_layer_button_toggled(bool p_pressed); Button *toggle_grid_button = nullptr; void _on_grid_toggled(bool p_pressed); + + enum { + ADVANCED_MENU_REPLACE_WITH_PROXIES, + ADVANCED_MENU_EXTRACT_TILE_MAP_LAYERS, + }; MenuButton *advanced_menu_button = nullptr; void _advanced_menu_button_id_pressed(int p_id); // Bottom panel. - Label *missing_tileset_label = nullptr; + Label *cant_edit_label = nullptr; Tabs *tabs_bar = nullptr; LocalVector tabs_data; LocalVector tabs_plugins; @@ -393,7 +416,6 @@ private: // Updates. void _layers_select_next_or_previous(bool p_next); - void _update_highlighting_toggle(); // Inspector undo/redo callback. void _move_tile_map_array_element(Object *p_undo_redo, Object *p_edited, const String &p_array_prefix, int p_from_index, int p_to_pos); @@ -407,8 +429,8 @@ public: bool forward_canvas_gui_input(const Ref &p_event); void forward_canvas_draw_over_viewport(Control *p_overlay); - void edit(LayeredTileMapLayer *p_tile_map_layer); - void update_layers_selector(); + void edit(Object *p_tile_map_layer); + void set_show_layer_selector(bool p_show_layer_selector); LayeredTileMapLayerEditor(); ~LayeredTileMapLayerEditor(); diff --git a/modules/layered_tile_map/editor/layered_tiles_editor_plugin.cpp b/modules/layered_tile_map/editor/layered_tiles_editor_plugin.cpp index edf031747..6740d6df4 100644 --- a/modules/layered_tile_map/editor/layered_tiles_editor_plugin.cpp +++ b/modules/layered_tile_map/editor/layered_tiles_editor_plugin.cpp @@ -49,6 +49,7 @@ #include "scene/main/control.h" #include "scene/main/viewport.h" #include "scene/resources/texture.h" +#include "editor/multi_node_edit.h" LayeredTilesEditorUtils *LayeredTilesEditorUtils::singleton = nullptr; LayeredTileMapEditorPlugin *tile_map_plugin_singleton = nullptr; @@ -356,7 +357,7 @@ void LayeredTileMapEditorPlugin::_tile_map_layer_removed() { void LayeredTileMapEditorPlugin::_update_tile_map() { LayeredTileMapLayer *edited_layer = Object::cast_to(ObjectDB::get_instance(tile_map_layer_id)); if (edited_layer) { - Ref tile_set = edited_layer->get_effective_tile_set(); + Ref tile_set = edited_layer->get_tile_set(); if (tile_set.is_valid() && tile_set_id != tile_set->get_instance_id()) { tile_set_plugin_singleton->edit(tile_set.ptr()); tile_set_plugin_singleton->make_visible(true); @@ -375,39 +376,27 @@ void LayeredTileMapEditorPlugin::_select_layer(const StringName &p_name) { ERR_FAIL_NULL(edited_layer); Node *parent = edited_layer->get_parent(); - ERR_FAIL_NULL(parent); - - LayeredTileMapLayer *new_layer = Object::cast_to(parent->get_node_or_null(String(p_name))); - edit(new_layer); + + if (parent) { + LayeredTileMapLayer *new_layer = Object::cast_to(parent->get_node_or_null(String(p_name))); + edit(new_layer); + } } -void LayeredTileMapEditorPlugin::_edit_tile_map_layer(LayeredTileMapLayer *p_tile_map_layer) { +void LayeredTileMapEditorPlugin::_edit_tile_map_layer(LayeredTileMapLayer *p_tile_map_layer, bool p_show_layer_selector) { ERR_FAIL_NULL(p_tile_map_layer); editor->edit(p_tile_map_layer); - - // Update the selected layers in the LayeredTileMapLayerGroup parent node. - LayeredTileMapLayerGroup *tile_map_layer_group = Object::cast_to(p_tile_map_layer->get_parent()); - if (tile_map_layer_group) { - Vector selected; - selected.push_back(p_tile_map_layer->get_name()); - tile_map_layer_group->set_selected_layers(selected); - } + + editor->set_show_layer_selector(p_show_layer_selector); // Update the object IDs. tile_map_layer_id = p_tile_map_layer->get_instance_id(); p_tile_map_layer->connect("changed", this, "_tile_map_layer_changed"); p_tile_map_layer->connect("tree_exited", this, "_tile_map_layer_removed"); - if (tile_map_layer_group) { - tile_map_group_id = tile_map_layer_group->get_instance_id(); - tile_map_layer_group->connect("child_entered_tree", this, "_tile_map_group_child_tree_changed"); - tile_map_layer_group->connect("child_exiting_tree", this, "_tile_map_group_child_tree_changed"); - tile_map_layer_group->connect("child_order_changed", editor, "update_layers_selector"); - } - // Update the edited tileset. - Ref tile_set = p_tile_map_layer->get_effective_tile_set(); + Ref tile_set = p_tile_map_layer->get_tile_set(); if (tile_set.is_valid()) { tile_set_plugin_singleton->edit(tile_set.ptr()); tile_set_plugin_singleton->make_visible(true); @@ -418,30 +407,15 @@ void LayeredTileMapEditorPlugin::_edit_tile_map_layer(LayeredTileMapLayer *p_til } } -void LayeredTileMapEditorPlugin::_edit_tile_map_layer_group(LayeredTileMapLayerGroup *p_tile_map_layer_group) { - ERR_FAIL_NULL(p_tile_map_layer_group); +void LayeredTileMapEditorPlugin::_edit_tile_map(LayeredTileMap *p_tile_map) { + ERR_FAIL_NULL(p_tile_map); - Vector selected_layers = p_tile_map_layer_group->get_selected_layers(); - - LayeredTileMapLayer *selected_layer = nullptr; - if (selected_layers.size() > 0) { - // Edit the selected layer. - selected_layer = Object::cast_to(p_tile_map_layer_group->get_node_or_null(String(selected_layers[0]))); - } - if (!selected_layer) { - // Edit the first layer found. - for (int i = 0; i < p_tile_map_layer_group->get_child_count(); i++) { - selected_layer = Object::cast_to(p_tile_map_layer_group->get_child(i)); - if (selected_layer) { - break; - } - } - } - - if (selected_layer) { - _edit_tile_map_layer(selected_layer); + if (p_tile_map->get_layers_count() > 0) { + LayeredTileMapLayer *selected_layer = Object::cast_to(p_tile_map->get_child(0)); + _edit_tile_map_layer(selected_layer, true); } else { editor->edit(nullptr); + editor->set_show_layer_selector(false); } } @@ -451,14 +425,9 @@ void LayeredTileMapEditorPlugin::_notification(int p_notification) { } } -void LayeredTileMapEditorPlugin::_tile_map_group_child_tree_changed(Node *p_node) { - editor->update_layers_selector(); -} - void LayeredTileMapEditorPlugin::_bind_methods() { ClassDB::bind_method(D_METHOD("_tile_map_layer_changed"), &LayeredTileMapEditorPlugin::_tile_map_layer_changed); ClassDB::bind_method(D_METHOD("_tile_map_layer_removed"), &LayeredTileMapEditorPlugin::_tile_map_layer_removed); - ClassDB::bind_method(D_METHOD("_tile_map_group_child_tree_changed"), &LayeredTileMapEditorPlugin::_tile_map_group_child_tree_changed); ClassDB::bind_method(D_METHOD("_update_tile_map"), &LayeredTileMapEditorPlugin::_update_tile_map); ClassDB::bind_method(D_METHOD("_select_layer"), &LayeredTileMapEditorPlugin::_select_layer); } @@ -470,36 +439,39 @@ void LayeredTileMapEditorPlugin::edit(Object *p_object) { edited_layer->disconnect("tree_exited", this, "_tile_map_layer_removed"); } - LayeredTileMapLayerGroup *tile_map_group = Object::cast_to(ObjectDB::get_instance(tile_map_group_id)); - if (tile_map_group) { - tile_map_group->disconnect("child_entered_tree", this, "_tile_map_group_child_tree_changed"); - tile_map_group->disconnect("child_exiting_tree", this, "_tile_map_group_child_tree_changed"); - tile_map_group->disconnect("child_order_changed", editor, "update_layers_selector"); - } - tile_map_group_id = ObjectID(); tile_map_layer_id = ObjectID(); tile_set_id = ObjectID(); - LayeredTileMapLayerGroup *tile_map_layer_group = Object::cast_to(p_object); + LayeredTileMap *tile_map = Object::cast_to(p_object); LayeredTileMapLayer *tile_map_layer = Object::cast_to(p_object); - if (tile_map_layer_group) { - _edit_tile_map_layer_group(tile_map_layer_group); + + MultiNodeEdit *multi_node_edit = Object::cast_to(p_object); + if (tile_map) { + _edit_tile_map(tile_map); } else if (tile_map_layer) { - _edit_tile_map_layer(tile_map_layer); + _edit_tile_map_layer(tile_map_layer, false); + } else if (multi_node_edit) { + editor->edit(multi_node_edit); } else { - // Deselect the layer in the group. - if (edited_layer) { - tile_map_layer_group = Object::cast_to(edited_layer->get_parent()); - if (tile_map_layer_group) { - tile_map_layer_group->set_selected_layers(Vector()); - } - } + editor->edit(nullptr); } } bool LayeredTileMapEditorPlugin::handles(Object *p_object) const { - return Object::cast_to(p_object) != nullptr || Object::cast_to(p_object) != nullptr; + MultiNodeEdit *multi_node_edit = Object::cast_to(p_object); + Node *edited_scene = EditorNode::get_singleton()->get_edited_scene(); + if (multi_node_edit && edited_scene) { + bool only_tile_map_layers = true; + for (int i = 0; i < multi_node_edit->get_node_count(); i++) { + if (!Object::cast_to(edited_scene->get_node(multi_node_edit->get_node(i)))) { + only_tile_map_layers = false; + break; + } + } + return only_tile_map_layers; + } + return Object::cast_to(p_object) != nullptr || Object::cast_to(p_object) != nullptr; } void LayeredTileMapEditorPlugin::make_visible(bool p_visible) { @@ -542,7 +514,6 @@ LayeredTileMapEditorPlugin::LayeredTileMapEditorPlugin(EditorNode *p_node) { editor->set_h_size_flags(Control::SIZE_EXPAND_FILL); editor->set_v_size_flags(Control::SIZE_EXPAND_FILL); editor->set_custom_minimum_size(Size2(0, 200) * EDSCALE); - editor->connect("change_selected_layer_request", this, "_select_layer"); editor->hide(); button = EditorNode::get_singleton()->add_bottom_panel_item(TTR("LayeredTileMap"), editor); @@ -586,6 +557,7 @@ ObjectID LayeredTileSetEditorPlugin::get_edited_tileset() const { LayeredTileSetEditorPlugin::LayeredTileSetEditorPlugin(EditorNode *p_node) { EDITOR_DEF("editors/layered_tiles_editor/display_grid", true); + EDITOR_DEF("editors/layered_tiles_editor/highlight_selected_layer", true); EDITOR_DEF("editors/layered_tiles_editor/grid_color", Color(1.0, 0.5, 0.2, 0.5)); if (!LayeredTilesEditorUtils::get_singleton()) { diff --git a/modules/layered_tile_map/editor/layered_tiles_editor_plugin.h b/modules/layered_tile_map/editor/layered_tiles_editor_plugin.h index b2e3580a6..f5f6e20a9 100644 --- a/modules/layered_tile_map/editor/layered_tiles_editor_plugin.h +++ b/modules/layered_tile_map/editor/layered_tiles_editor_plugin.h @@ -134,10 +134,8 @@ class LayeredTileMapEditorPlugin : public EditorPlugin { void _update_tile_map(); void _select_layer(const StringName &p_name); - void _edit_tile_map_layer(LayeredTileMapLayer *p_tile_map_layer); - void _edit_tile_map_layer_group(LayeredTileMapLayerGroup *p_tile_map_layer_group); - - void _tile_map_group_child_tree_changed(Node *p_node); + void _edit_tile_map_layer(LayeredTileMapLayer *p_tile_map_layer, bool p_show_layer_selector); + void _edit_tile_map(LayeredTileMap *p_tile_map); protected: void _notification(int p_notification); diff --git a/modules/layered_tile_map/icons/icon_layered_tile_map_layer.svg b/modules/layered_tile_map/icons/icon_layered_tile_map_layer.svg index 1903a87e3..90664dee0 100644 --- a/modules/layered_tile_map/icons/icon_layered_tile_map_layer.svg +++ b/modules/layered_tile_map/icons/icon_layered_tile_map_layer.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/modules/layered_tile_map/layered_tile_map.cpp b/modules/layered_tile_map/layered_tile_map.cpp index 89a84f4f0..8864e6291 100644 --- a/modules/layered_tile_map/layered_tile_map.cpp +++ b/modules/layered_tile_map/layered_tile_map.cpp @@ -35,6 +35,7 @@ #include "core/core_string_names.h" #include "layered_tile_map_layer.h" #include "scene/main/control.h" +#include "core/io/marshalls.h" #define TILEMAP_CALL_FOR_LAYER(layer, function, ...) \ if (layer < 0) { \ @@ -50,10 +51,118 @@ ERR_FAIL_INDEX_V(layer, (int)layers.size(), err_value); \ return layers[layer]->function(__VA_ARGS__); +void LayeredTileMap::_tile_set_changed() { + update_configuration_warning(); +} + void LayeredTileMap::_emit_changed() { emit_signal(CoreStringNames::get_singleton()->changed); } +void LayeredTileMap::_set_tile_map_data_using_compatibility_format(int p_layer, LayeredTileMapDataFormat p_format, const Vector &p_data) { + ERR_FAIL_INDEX(p_layer, (int)layers.size()); + ERR_FAIL_COND(p_format >= LayeredTileMapDataFormat::TILE_MAP_DATA_FORMAT_MAX); +#ifndef DISABLE_DEPRECATED + ERR_FAIL_COND_MSG(p_format != (LayeredTileMapDataFormat)(TILE_MAP_DATA_FORMAT_MAX - 1), "Old TileMap data format detected despite DISABLE_DEPRECATED being set compilation time."); +#endif // DISABLE_DEPRECATED + + // Set data for a given tile from raw data. + int c = p_data.size(); + const int *r = p_data.ptr(); + + int offset = (p_format >= LayeredTileMapDataFormat::TILE_MAP_DATA_FORMAT_2) ? 3 : 2; + ERR_FAIL_COND_MSG(c % offset != 0, vformat("Corrupted tile data. Got size: %d. Expected modulo: %d", c, offset)); + + layers[p_layer]->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 < ((p_format >= LayeredTileMapDataFormat::TILE_MAP_DATA_FORMAT_2) ? 12 : 8); j++) { + local[j] = ptr[j]; + } + +#ifdef BIG_ENDIAN_ENABLED + SWAP(local[0], local[3]); + SWAP(local[1], local[2]); + SWAP(local[4], local[7]); + SWAP(local[5], local[6]); + //TODO: ask someone to check this... + if (FORMAT >= FORMAT_2) { + SWAP(local[8], local[11]); + SWAP(local[9], local[10]); + } +#endif + // Extracts position in TileMap. + int16_t x = decode_uint16(&local[0]); + int16_t y = decode_uint16(&local[2]); + + if (p_format == LayeredTileMapDataFormat::TILE_MAP_DATA_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_uint16(&local[8]); + uint16_t alternative_tile = decode_uint16(&local[10]); + layers[p_layer]->set_cell(Vector2i(x, y), source_id, Vector2i(atlas_coords_x, atlas_coords_y), alternative_tile); + } else { +#ifndef DISABLE_DEPRECATED + // Previous decated format. + uint32_t v = decode_uint32(&local[4]); + // Extract the transform flags that used to be in the tilemap. + bool flip_h = v & (1UL << 29); + bool flip_v = v & (1UL << 30); + bool transpose = v & (1UL << 31); + v &= (1UL << 29) - 1; + + // Extract autotile/atlas coords. + int16_t coord_x = 0; + int16_t coord_y = 0; + if (p_format == LayeredTileMapDataFormat::TILE_MAP_DATA_FORMAT_2) { + coord_x = decode_uint16(&local[8]); + coord_y = decode_uint16(&local[10]); + } + + if (tile_set.is_valid()) { + Array a = tile_set->compatibility_tilemap_map(v, Vector2i(coord_x, coord_y), flip_h, flip_v, transpose); + if (a.size() == 3) { + layers[p_layer]->set_cell(Vector2i(x, y), a[0], a[1], a[2]); + } else { + ERR_PRINT(vformat("No valid tile in Tileset for: tile:%s coords:%s flip_h:%s flip_v:%s transpose:%s", v, Vector2i(coord_x, coord_y), flip_h, flip_v, transpose)); + } + } else { + int compatibility_alternative_tile = ((int)flip_h) + ((int)flip_v << 1) + ((int)transpose << 2); + layers[p_layer]->set_cell(Vector2i(x, y), v, Vector2i(coord_x, coord_y), compatibility_alternative_tile); + } +#endif // DISABLE_DEPRECATED + } + } +} + +Vector LayeredTileMap::_get_tile_map_data_using_compatibility_format(int p_layer) const { + ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), Vector()); + + // Export tile data to raw format. + const HashMap tile_map_layer_data = layers[p_layer]->get_tile_map_layer_data(); + Vector tile_data; + tile_data.resize(tile_map_layer_data.size() * 3); + int *w = tile_data.ptrw(); + + // Save in highest format. + + int idx = 0; + for (const HashMap::Element *E = tile_map_layer_data.front(); E; E = E->next) { + uint8_t *ptr = (uint8_t *)&w[idx]; + encode_uint16((int16_t)(E->key().x), &ptr[0]); + encode_uint16((int16_t)(E->key().y), &ptr[2]); + encode_uint16(E->value().cell.source_id, &ptr[4]); + encode_uint16(E->value().cell.coord_x, &ptr[6]); + encode_uint16(E->value().cell.coord_y, &ptr[8]); + encode_uint16(E->value().cell.alternative_tile, &ptr[10]); + idx += 3; + } + + return tile_data; +} + void LayeredTileMap::_notification(int p_what) { switch (p_what) { case LayeredTileMap::NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { @@ -228,6 +337,34 @@ void LayeredTileMap::draw_tile(RID p_canvas_item, const Vector2 &p_position, con } } +void LayeredTileMap::set_tileset(const Ref &p_tileset) { + if (p_tileset == tile_set) { + return; + } + + // Set the tileset, registering to its changes. + if (tile_set.is_valid()) { + tile_set->disconnect("changed", this, "_tile_set_changed"); + } + + tile_set = p_tileset; + + if (tile_set.is_valid()) { + tile_set->connect("changed", this, "_tile_set_changed"); + } + + for (int i = 0; i < get_child_count(); i++) { + LayeredTileMapLayer *layer = Object::cast_to(get_child(i)); + if (layer) { + layer->set_tile_set(tile_set); + } + } +} + +Ref LayeredTileMap::get_tileset() const { + return tile_set; +} + int LayeredTileMap::get_layers_count() const { return layers.size(); } @@ -245,6 +382,7 @@ void LayeredTileMap::add_layer(int p_to_pos) { add_child(new_layer, false); move_child(new_layer, 0); new_layer->set_name(vformat("Layer%d", p_to_pos)); + new_layer->set_tile_set(tile_set); move_child(new_layer, p_to_pos); for (uint32_t i = 0; i < layers.size(); i++) { layers[i]->set_as_tile_map_internal_node(i); @@ -282,9 +420,12 @@ void LayeredTileMap::remove_layer(int p_layer) { ERR_FAIL_INDEX(p_layer, (int)layers.size()); // Clear before removing the layer. - layers[p_layer]->queue_delete(); + LayeredTileMapLayer *removed = layers[p_layer]; layers.remove(p_layer); + remove_child(removed); + removed->queue_delete(); + for (uint32_t i = 0; i < layers.size(); i++) { layers[i]->set_as_tile_map_internal_node(i); } @@ -387,7 +528,7 @@ void LayeredTileMap::set_collision_visibility_mode(LayeredTileMap::VisibilityMod for (uint32_t i = 0; i < layers.size(); ++i) { LayeredTileMapLayer *layer = layers[i]; - layer->set_collision_visibility_mode(LayeredTileMapLayer::VisibilityMode(p_show_collision)); + layer->set_collision_visibility_mode(LayeredTileMapLayer::DebugVisibilityMode(p_show_collision)); } _emit_changed(); @@ -405,7 +546,7 @@ void LayeredTileMap::set_navigation_visibility_mode(LayeredTileMap::VisibilityMo for (uint32_t i = 0; i < layers.size(); ++i) { LayeredTileMapLayer *layer = layers[i]; - layer->set_navigation_visibility_mode(LayeredTileMapLayer::VisibilityMode(p_show_navigation)); + layer->set_navigation_visibility_mode(LayeredTileMapLayer::DebugVisibilityMode(p_show_navigation)); } _emit_changed(); @@ -441,19 +582,85 @@ void LayeredTileMap::erase_cell(int p_layer, const Vector2i &p_coords) { } int LayeredTileMap::get_cell_source_id(int p_layer, const Vector2i &p_coords, bool p_use_proxies) const { - TILEMAP_CALL_FOR_LAYER_V(p_layer, LayeredTileSet::INVALID_SOURCE, get_cell_source_id, p_coords, p_use_proxies); + if (p_use_proxies && tile_set.is_valid()) { + if (p_layer < 0) { + p_layer = layers.size() + p_layer; + } + ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), LayeredTileSet::INVALID_SOURCE); + + int source_id = layers[p_layer]->get_cell_source_id(p_coords); + Vector2i atlas_coords = layers[p_layer]->get_cell_atlas_coords(p_coords); + int alternative_id = layers[p_layer]->get_cell_alternative_tile(p_coords); + + Array arr = tile_set->map_tile_proxy(source_id, atlas_coords, alternative_id); + ERR_FAIL_COND_V(arr.size() != 3, LayeredTileSet::INVALID_SOURCE); + return arr[0]; + } else { + TILEMAP_CALL_FOR_LAYER_V(p_layer, LayeredTileSet::INVALID_SOURCE, get_cell_source_id, p_coords); + } } Vector2i LayeredTileMap::get_cell_atlas_coords(int p_layer, const Vector2i &p_coords, bool p_use_proxies) const { - TILEMAP_CALL_FOR_LAYER_V(p_layer, LayeredTileSetSource::INVALID_ATLAS_COORDS, get_cell_atlas_coords, p_coords, p_use_proxies); + if (p_use_proxies && tile_set.is_valid()) { + if (p_layer < 0) { + p_layer = layers.size() + p_layer; + } + ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), LayeredTileSetAtlasSource::INVALID_ATLAS_COORDS); + + int source_id = layers[p_layer]->get_cell_source_id(p_coords); + Vector2i atlas_coords = layers[p_layer]->get_cell_atlas_coords(p_coords); + int alternative_id = layers[p_layer]->get_cell_alternative_tile(p_coords); + + Array arr = tile_set->map_tile_proxy(source_id, atlas_coords, alternative_id); + ERR_FAIL_COND_V(arr.size() != 3, LayeredTileSetSource::INVALID_ATLAS_COORDS); + return arr[1]; + } else { + TILEMAP_CALL_FOR_LAYER_V(p_layer, LayeredTileSetSource::INVALID_ATLAS_COORDS, get_cell_atlas_coords, p_coords); + } } int LayeredTileMap::get_cell_alternative_tile(int p_layer, const Vector2i &p_coords, bool p_use_proxies) const { - TILEMAP_CALL_FOR_LAYER_V(p_layer, LayeredTileSetSource::INVALID_TILE_ALTERNATIVE, get_cell_alternative_tile, p_coords, p_use_proxies); + if (p_use_proxies && tile_set.is_valid()) { + if (p_layer < 0) { + p_layer = layers.size() + p_layer; + } + ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), LayeredTileSetSource::INVALID_TILE_ALTERNATIVE); + + int source_id = layers[p_layer]->get_cell_source_id(p_coords); + Vector2i atlas_coords = layers[p_layer]->get_cell_atlas_coords(p_coords); + int alternative_id = layers[p_layer]->get_cell_alternative_tile(p_coords); + + Array arr = tile_set->map_tile_proxy(source_id, atlas_coords, alternative_id); + ERR_FAIL_COND_V(arr.size() != 3, LayeredTileSetSource::INVALID_TILE_ALTERNATIVE); + return arr[2]; + } else { + TILEMAP_CALL_FOR_LAYER_V(p_layer, LayeredTileSetSource::INVALID_TILE_ALTERNATIVE, get_cell_alternative_tile, p_coords); + } } LayeredTileData *LayeredTileMap::get_cell_tile_data(int p_layer, const Vector2i &p_coords, bool p_use_proxies) const { - TILEMAP_CALL_FOR_LAYER_V(p_layer, nullptr, get_cell_tile_data, p_coords, p_use_proxies); + if (p_use_proxies && tile_set.is_valid()) { + if (p_layer < 0) { + p_layer = layers.size() + p_layer; + } + ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), nullptr); + + int source_id = layers[p_layer]->get_cell_source_id(p_coords); + Vector2i atlas_coords = layers[p_layer]->get_cell_atlas_coords(p_coords); + int alternative_id = layers[p_layer]->get_cell_alternative_tile(p_coords); + + Array arr = tile_set->map_tile_proxy(source_id, atlas_coords, alternative_id); + ERR_FAIL_COND_V(arr.size() != 3, nullptr); + + Ref atlas_source = tile_set->get_source(arr[0]); + if (atlas_source.is_valid()) { + return atlas_source->get_tile_data(arr[1], arr[2]); + } else { + return nullptr; + } + } else { + TILEMAP_CALL_FOR_LAYER_V(p_layer, nullptr, get_cell_tile_data, p_coords); + } } Ref LayeredTileMap::get_pattern(int p_layer, PoolVector2iArray p_coords_array) { @@ -616,7 +823,10 @@ void LayeredTileMap::rao_setup_noise(Ref noise) { #endif LayeredTileMapCell LayeredTileMap::get_cell(int p_layer, const Vector2i &p_coords, bool p_use_proxies) const { - TILEMAP_CALL_FOR_LAYER_V(p_layer, LayeredTileMapCell(), get_cell, p_coords, p_use_proxies); + if (p_use_proxies) { + WARN_DEPRECATED_MSG("use_proxies is deprecated."); + } + TILEMAP_CALL_FOR_LAYER_V(p_layer, LayeredTileMapCell(), get_cell, p_coords); } Vector2i LayeredTileMap::get_coords_for_body_rid(RID p_physics_body) { @@ -647,6 +857,13 @@ void LayeredTileMap::fix_invalid_tiles() { } } +#ifdef TOOLS_ENABLED +LayeredTileMapLayer *LayeredTileMap::duplicate_layer_from_internal(int p_layer) { + ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), nullptr); + return Object::cast_to(layers[p_layer]->duplicate(DUPLICATE_USE_INSTANCING | DUPLICATE_FROM_EDITOR)); +} +#endif // TOOLS_ENABLED + void LayeredTileMap::clear_layer(int p_layer) { TILEMAP_CALL_FOR_LAYER(p_layer, clear) } @@ -713,10 +930,11 @@ bool LayeredTileMap::_set(const StringName &p_name, const Variant &p_value) { move_child(new_layer, 0); new_layer->set_as_tile_map_internal_node(0); new_layer->set_name("Layer0"); + new_layer->set_tile_set(tile_set); new_layer->connect(CoreStringNames::get_singleton()->changed, this, "_emit_changed"); layers.push_back(new_layer); } - layers[0]->set_tile_data(format, p_value); + _set_tile_map_data_using_compatibility_format(0, format, p_value); _emit_changed(); return true; } @@ -739,6 +957,7 @@ bool LayeredTileMap::_set(const StringName &p_name, const Variant &p_value) { move_child(new_layer, 0); new_layer->set_as_tile_map_internal_node(index); new_layer->set_name(vformat("Layer%d", index)); + new_layer->set_tile_set(tile_set); new_layer->connect(CoreStringNames::get_singleton()->changed, this, "_emit_changed"); layers.push_back(new_layer); } @@ -770,7 +989,7 @@ bool LayeredTileMap::_set(const StringName &p_name, const Variant &p_value) { set_layer_navigation_enabled(index, p_value); return true; } else if (components[1] == "tile_data") { - layers[index]->set_tile_data(format, p_value); + _set_tile_map_data_using_compatibility_format(index, format, p_value); _emit_changed(); return true; } else { @@ -783,7 +1002,7 @@ bool LayeredTileMap::_set(const StringName &p_name, const Variant &p_value) { bool LayeredTileMap::_get(const StringName &p_name, Variant &r_ret) const { Vector components = String(p_name).split("/", true, 2); if (p_name == "format") { - r_ret = LayeredTileMapDataFormat::FORMAT_MAX - 1; // When saving, always save highest format. + r_ret = LayeredTileMapDataFormat::TILE_MAP_DATA_FORMAT_MAX - 1; // When saving, always save highest format. return true; } #ifndef DISABLE_DEPRECATED @@ -820,7 +1039,7 @@ bool LayeredTileMap::_get(const StringName &p_name, Variant &r_ret) const { r_ret = is_layer_navigation_enabled(index); return true; } else if (components[1] == "tile_data") { - r_ret = layers[index]->get_tile_data(); + r_ret = _get_tile_map_data_using_compatibility_format(index); return true; } else { return false; @@ -1079,6 +1298,9 @@ void LayeredTileMap::_bind_methods() { ClassDB::bind_method(D_METHOD("force_update", "layer"), &LayeredTileMap::force_update, DEFVAL(-1)); #endif // DISABLE_DEPRECATED + ClassDB::bind_method(D_METHOD("set_tileset", "tileset"), &LayeredTileMap::set_tileset); + ClassDB::bind_method(D_METHOD("get_tileset"), &LayeredTileMap::get_tileset); + ClassDB::bind_method(D_METHOD("set_rendering_quadrant_size", "size"), &LayeredTileMap::set_rendering_quadrant_size); ClassDB::bind_method(D_METHOD("get_rendering_quadrant_size"), &LayeredTileMap::get_rendering_quadrant_size); @@ -1159,7 +1381,9 @@ void LayeredTileMap::_bind_methods() { //ClassDB::bind_method(D_METHOD("tile_data_runtime_update", "layer", "coords", "tile_data"), &LayeredTileMap::tile_data_runtime_update); ClassDB::bind_method(D_METHOD("_emit_changed"), &LayeredTileMap::_emit_changed); - + ClassDB::bind_method(D_METHOD("_tile_set_changed"), &LayeredTileMap::_tile_set_changed); + + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "tile_set", PROPERTY_HINT_RESOURCE_TYPE, "LayeredTileSet"), "set_tileset", "get_tileset"); ADD_PROPERTY(PropertyInfo(Variant::INT, "rendering_quadrant_size", PROPERTY_HINT_RANGE, "1,128,1"), "set_rendering_quadrant_size", "get_rendering_quadrant_size"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collision_animatable"), "set_collision_animatable", "is_collision_animatable"); ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_visibility_mode", PROPERTY_HINT_ENUM, "Default,Force Show,Force Hide"), "set_collision_visibility_mode", "get_collision_visibility_mode"); @@ -1191,7 +1415,7 @@ void LayeredTileMap::_bind_methods() { ADD_ARRAY("layers", "layer_"); - ADD_PROPERTY_DEFAULT("format", LayeredTileMapDataFormat::FORMAT_1); + ADD_PROPERTY_DEFAULT("format", LayeredTileMapDataFormat::TILE_MAP_DATA_FORMAT_1); ADD_SIGNAL(MethodInfo(CoreStringNames::get_singleton()->changed)); @@ -1206,6 +1430,7 @@ LayeredTileMap::LayeredTileMap() { move_child(new_layer, 0); new_layer->set_as_tile_map_internal_node(0); new_layer->set_name("Layer0"); + new_layer->set_tile_set(tile_set); new_layer->connect(CoreStringNames::get_singleton()->changed, this, "_emit_changed"); layers.push_back(new_layer); default_layer = memnew(LayeredTileMapLayer); diff --git a/modules/layered_tile_map/layered_tile_map.h b/modules/layered_tile_map/layered_tile_map.h index dcedfb798..3a8e42ea1 100644 --- a/modules/layered_tile_map/layered_tile_map.h +++ b/modules/layered_tile_map/layered_tile_map.h @@ -32,7 +32,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "layered_tile_map_layer_group.h" +#include "layered_tile_map_layer.h" #include "layered_tile_set.h" #include "modules/modules_enabled.gen.h" @@ -47,14 +47,14 @@ class LayeredTileMapLayer; class TerrainConstraint; enum LayeredTileMapDataFormat { - FORMAT_1 = 0, - FORMAT_2, - FORMAT_3, - FORMAT_MAX, + TILE_MAP_DATA_FORMAT_1 = 0, + TILE_MAP_DATA_FORMAT_2, + TILE_MAP_DATA_FORMAT_3, + TILE_MAP_DATA_FORMAT_MAX, }; -class LayeredTileMap : public LayeredTileMapLayerGroup { - GDCLASS(LayeredTileMap, LayeredTileMapLayerGroup) +class LayeredTileMap : public YSort { + GDCLASS(LayeredTileMap, YSort) public: // Kept for compatibility, but should use LayeredTileMapLayer::VisibilityMode instead. @@ -68,11 +68,12 @@ private: friend class LayeredTileSetPlugin; // A compatibility enum to specify how is the data if formatted. - mutable LayeredTileMapDataFormat format = LayeredTileMapDataFormat::FORMAT_3; + mutable LayeredTileMapDataFormat format = LayeredTileMapDataFormat::TILE_MAP_DATA_FORMAT_3; static constexpr float FP_ADJUST = 0.00001; // Properties. + Ref tile_set; int rendering_quadrant_size = 16; bool collision_animatable = false; VisibilityMode collision_visibility_mode = VISIBILITY_MODE_DEFAULT; @@ -100,7 +101,13 @@ private: Ref _rao_noise; #endif + void _tile_set_changed(); + void _emit_changed(); + + // Kept for compatibility with TileMap. With TileMapLayers as individual nodes, the format is stored directly in the array. + void _set_tile_map_data_using_compatibility_format(int p_layer, LayeredTileMapDataFormat p_format, const Vector &p_data); + Vector _get_tile_map_data_using_compatibility_format(int p_layer) const; protected: bool _set(const StringName &p_name, const Variant &p_value); @@ -126,6 +133,10 @@ public: static void draw_tile(RID p_canvas_item, const Vector2 &p_position, const Ref p_tile_set, int p_atlas_source_id, const Vector2i &p_atlas_coords, int p_alternative_tile, int p_frame = -1, Color p_modulation = Color(1.0, 1.0, 1.0, 1.0), const LayeredTileData *p_tile_data_override = nullptr, real_t p_normalized_animation_offset = 0.0); + // Accessors. + void set_tileset(const Ref &p_tileset); + Ref get_tileset() const; + // Layers management. int get_layers_count() const; void add_layer(int p_to_pos); @@ -230,6 +241,11 @@ public: // Fixing and clearing methods. void fix_invalid_tiles(); +#ifdef TOOLS_ENABLED + // Moving layers outside of TileMap. + LayeredTileMapLayer *duplicate_layer_from_internal(int p_layer); +#endif // TOOLS_ENABLED + // Clears tiles from a given layer. void clear_layer(int p_layer); void clear(); diff --git a/modules/layered_tile_map/layered_tile_map_layer.cpp b/modules/layered_tile_map/layered_tile_map_layer.cpp index 446a1fa07..bbfba19d9 100644 --- a/modules/layered_tile_map/layered_tile_map_layer.cpp +++ b/modules/layered_tile_map/layered_tile_map_layer.cpp @@ -31,6 +31,8 @@ #include "layered_tile_map_layer.h" +#include "layered_tile_map.h" + #include "core/config/engine.h" #include "core/containers/hash_set.h" #include "core/core_string_names.h" @@ -58,7 +60,6 @@ Vector2i LayeredTileMapLayer::_coords_to_debug_quadrant_coords(const Vector2i &p } void LayeredTileMapLayer::_debug_update() { - const Ref &tile_set = get_effective_tile_set(); RenderingServer *rs = RenderingServer::get_singleton(); // Check if we should cleanup everything. @@ -92,7 +93,7 @@ void LayeredTileMapLayer::_debug_update() { if (_debug_was_cleaned_up || anything_changed) { // Update all cells. - for (HashMap::Element *kv = tile_map.front(); kv; kv = kv->next) { + for (HashMap::Element *kv = tile_map_layer_data.front(); kv; kv = kv->next) { CellData &cell_data = kv->value(); _debug_quadrants_update_cell(cell_data, dirty_debug_quadrant_list); } @@ -187,7 +188,6 @@ void LayeredTileMapLayer::_debug_quadrants_update_cell(CellData &r_cell_data, Se /////////////////////////////// Rendering ////////////////////////////////////// void LayeredTileMapLayer::_rendering_update() { - const Ref &tile_set = get_effective_tile_set(); RenderingServer *rs = RenderingServer::get_singleton(); // Check if we should cleanup everything. @@ -198,22 +198,11 @@ void LayeredTileMapLayer::_rendering_update() { // Modulate the layer. Color layer_modulate = get_modulate(); #ifdef TOOLS_ENABLED - const LayeredTileMapLayerGroup *tile_map_layer_group = Object::cast_to(get_parent()); - if (tile_map_layer_group) { - const Vector selected_layers = tile_map_layer_group->get_selected_layers(); - if (tile_map_layer_group->is_highlighting_selected_layer() && selected_layers.size() == 1 && get_name() != selected_layers[0]) { - LayeredTileMapLayer *selected_layer = Object::cast_to(tile_map_layer_group->get_node_or_null(String(selected_layers[0]))); - if (selected_layer) { - int z_selected = selected_layer->get_z_index(); - int layer_z_index = get_z_index(); - if (layer_z_index < z_selected || (layer_z_index == z_selected && get_index() < selected_layer->get_index())) { - layer_modulate = layer_modulate.darkened(0.5); - } else if (layer_z_index > z_selected || (layer_z_index == z_selected && get_index() > selected_layer->get_index())) { - layer_modulate = layer_modulate.darkened(0.5); - layer_modulate.a *= 0.3; - } - } - } + if (highlight_mode == HIGHLIGHT_MODE_BELOW) { + layer_modulate = layer_modulate.darkened(0.5); + } else if (highlight_mode == HIGHLIGHT_MODE_ABOVE) { + layer_modulate = layer_modulate.darkened(0.5); + layer_modulate.a *= 0.3; } #endif // TOOLS_ENABLED rs->canvas_item_set_modulate(get_canvas_item(), layer_modulate); @@ -227,7 +216,7 @@ void LayeredTileMapLayer::_rendering_update() { // Check if anything changed that might change the quadrant shape. // If so, recreate everything. bool quandrant_shape_changed = dirty.flags[DIRTY_FLAGS_LAYER_RENDERING_QUADRANT_SIZE] || - (is_sort_enabled() && (dirty.flags[DIRTY_FLAGS_LAYER_Y_SORT_ENABLED] || dirty.flags[DIRTY_FLAGS_LAYER_Y_SORT_ORIGIN] || dirty.flags[DIRTY_FLAGS_LAYER_LOCAL_TRANSFORM] || dirty.flags[DIRTY_FLAGS_LAYER_GROUP_TILE_SET] + (is_sort_enabled() && (dirty.flags[DIRTY_FLAGS_LAYER_Y_SORT_ENABLED] || dirty.flags[DIRTY_FLAGS_LAYER_Y_SORT_ORIGIN] || dirty.flags[DIRTY_FLAGS_LAYER_LOCAL_TRANSFORM] || dirty.flags[DIRTY_FLAGS_TILE_SET] #ifdef MODULE_VERTEX_LIGHTS_2D_ENABLED || dirty.flags[DIRTY_FLAGS_LAYER_VERTEX_LIGHTS] @@ -257,9 +246,9 @@ void LayeredTileMapLayer::_rendering_update() { if (!forced_cleanup) { // List all quadrants to update, recreating them if needed. - if (dirty.flags[DIRTY_FLAGS_LAYER_GROUP_TILE_SET] || dirty.flags[DIRTY_FLAGS_LAYER_IN_TREE] || _rendering_was_cleaned_up) { + if (dirty.flags[DIRTY_FLAGS_TILE_SET] || dirty.flags[DIRTY_FLAGS_LAYER_IN_TREE] || _rendering_was_cleaned_up) { // Update all cells. - for (HashMap::Element *kv = tile_map.front(); kv; kv = kv->next) { + for (HashMap::Element *kv = tile_map_layer_data.front(); kv; kv = kv->next) { CellData &cell_data = kv->value(); _rendering_quadrants_update_cell(cell_data, dirty_rendering_quadrant_list); } @@ -455,13 +444,13 @@ void LayeredTileMapLayer::_rendering_update() { // ----------- Occluders processing ----------- if (forced_cleanup) { // Clean everything. - for (HashMap::Element *kv = tile_map.front(); kv; kv = kv->next) { + for (HashMap::Element *kv = tile_map_layer_data.front(); kv; kv = kv->next) { _rendering_occluders_clear_cell(kv->value()); } } else { - if (_rendering_was_cleaned_up || dirty.flags[DIRTY_FLAGS_LAYER_GROUP_TILE_SET]) { + if (_rendering_was_cleaned_up || dirty.flags[DIRTY_FLAGS_TILE_SET]) { // Update all cells. - for (HashMap::Element *kv = tile_map.front(); kv; kv = kv->next) { + for (HashMap::Element *kv = tile_map_layer_data.front(); kv; kv = kv->next) { _rendering_occluders_update_cell(kv->value()); } } else { @@ -480,11 +469,10 @@ void LayeredTileMapLayer::_rendering_update() { void LayeredTileMapLayer::_rendering_notification(int p_what) { RenderingServer *rs = RenderingServer::get_singleton(); - const Ref &tile_set = get_effective_tile_set(); if (p_what == NOTIFICATION_TRANSFORM_CHANGED || p_what == NOTIFICATION_ENTER_CANVAS || p_what == NOTIFICATION_VISIBILITY_CHANGED) { if (tile_set.is_valid()) { Transform2D tilemap_xform = get_global_transform(); - for (HashMap::Element *kv = tile_map.front(); kv; kv = kv->next) { + for (HashMap::Element *kv = tile_map_layer_data.front(); kv; kv = kv->next) { const CellData &cell_data = kv->value(); for (uint32_t i = 0; i < cell_data.occluders.size(); ++i) { @@ -504,8 +492,6 @@ void LayeredTileMapLayer::_rendering_notification(int p_what) { } void LayeredTileMapLayer::_rendering_quadrants_update_cell(CellData &r_cell_data, SelfList::List &r_dirty_rendering_quadrant_list) { - const Ref &tile_set = get_effective_tile_set(); - // Check if the cell is valid and retrieve its y_sort_origin. bool is_valid = false; int tile_y_sort_origin = 0; @@ -637,7 +623,6 @@ void LayeredTileMapLayer::_rendering_occluders_clear_cell(CellData &r_cell_data) } void LayeredTileMapLayer::_rendering_occluders_update_cell(CellData &r_cell_data) { - const Ref &tile_set = get_effective_tile_set(); RenderingServer *rs = RenderingServer::get_singleton(); // Free unused occluders then resize the occluders array. @@ -706,7 +691,6 @@ void LayeredTileMapLayer::_rendering_occluders_update_cell(CellData &r_cell_data #ifdef DEBUG_ENABLED void LayeredTileMapLayer::_rendering_draw_cell_debug(const RID &p_canvas_item, const Vector2 &p_quadrant_pos, const CellData &r_cell_data) { - const Ref &tile_set = get_effective_tile_set(); ERR_FAIL_COND(!tile_set.is_valid()); if (!Engine::get_singleton()->is_editor_hint()) { @@ -755,19 +739,17 @@ void LayeredTileMapLayer::_rendering_draw_cell_debug(const RID &p_canvas_item, c /////////////////////////////// Physics ////////////////////////////////////// void LayeredTileMapLayer::_physics_update() { - const Ref &tile_set = get_effective_tile_set(); - // Check if we should cleanup everything. - bool forced_cleanup = in_destructor || !enabled || !is_inside_tree() || !tile_set.is_valid(); + bool forced_cleanup = in_destructor || !enabled || !collision_enabled || !is_inside_tree() || !tile_set.is_valid(); if (forced_cleanup) { // Clean everything. - for (HashMap::Element *kv = tile_map.front(); kv; kv = kv->next) { + for (HashMap::Element *kv = tile_map_layer_data.front(); kv; kv = kv->next) { _physics_clear_cell(kv->value()); } } else { - if (_physics_was_cleaned_up || dirty.flags[DIRTY_FLAGS_LAYER_GROUP_TILE_SET] || dirty.flags[DIRTY_FLAGS_LAYER_USE_KINEMATIC_BODIES] || dirty.flags[DIRTY_FLAGS_LAYER_IN_TREE]) { + if (_physics_was_cleaned_up || dirty.flags[DIRTY_FLAGS_TILE_SET] || dirty.flags[DIRTY_FLAGS_LAYER_USE_KINEMATIC_BODIES] || dirty.flags[DIRTY_FLAGS_LAYER_IN_TREE]) { // Update all cells. - for (HashMap::Element *kv = tile_map.front(); kv; kv = kv->next) { + for (HashMap::Element *kv = tile_map_layer_data.front(); kv; kv = kv->next) { _physics_update_cell(kv->value()); } } else { @@ -785,7 +767,6 @@ void LayeredTileMapLayer::_physics_update() { } void LayeredTileMapLayer::_physics_notification(int p_what) { - const Ref &tile_set = get_effective_tile_set(); Transform2D gl_transform = get_global_transform(); Physics2DServer *ps = Physics2DServer::get_singleton(); @@ -794,7 +775,7 @@ void LayeredTileMapLayer::_physics_notification(int p_what) { case NOTIFICATION_TRANSFORM_CHANGED: // Move the collisison shapes along with the LayeredTileMap. if (is_inside_tree() && tile_set.is_valid()) { - for (HashMap::Element *kv = tile_map.front(); kv; kv = kv->next) { + for (HashMap::Element *kv = tile_map_layer_data.front(); kv; kv = kv->next) { const CellData &cell_data = kv->value(); for (uint32_t i = 0; i < cell_data.bodies.size(); ++i) { @@ -814,7 +795,7 @@ void LayeredTileMapLayer::_physics_notification(int p_what) { if (is_inside_tree()) { RID space = get_world_2d()->get_space(); - for (HashMap::Element *kv = tile_map.front(); kv; kv = kv->next) { + for (HashMap::Element *kv = tile_map_layer_data.front(); kv; kv = kv->next) { const CellData &cell_data = kv->value(); for (uint32_t i = 0; i < cell_data.bodies.size(); ++i) { @@ -848,7 +829,6 @@ void LayeredTileMapLayer::_physics_clear_cell(CellData &r_cell_data) { } void LayeredTileMapLayer::_physics_update_cell(CellData &r_cell_data) { - const Ref &tile_set = get_effective_tile_set(); Transform2D gl_transform = get_global_transform(); RID space = get_world_2d()->get_space(); Physics2DServer *ps = Physics2DServer::get_singleton(); @@ -965,7 +945,6 @@ void LayeredTileMapLayer::_physics_update_cell(CellData &r_cell_data) { #ifdef DEBUG_ENABLED void LayeredTileMapLayer::_physics_draw_cell_debug(const RID &p_canvas_item, const Vector2 &p_quadrant_pos, const CellData &r_cell_data) { // Draw the debug collision shapes. - const Ref &tile_set = get_effective_tile_set(); ERR_FAIL_COND(!tile_set.is_valid()); if (!get_tree()) { @@ -974,13 +953,13 @@ void LayeredTileMapLayer::_physics_draw_cell_debug(const RID &p_canvas_item, con bool show_collision = false; switch (collision_visibility_mode) { - case LayeredTileMapLayer::VISIBILITY_MODE_DEFAULT: + case LayeredTileMapLayer::DEBUG_VISIBILITY_MODE_DEFAULT: show_collision = !Engine::get_singleton()->is_editor_hint() && get_tree()->is_debugging_collisions_hint(); break; - case LayeredTileMapLayer::VISIBILITY_MODE_FORCE_HIDE: + case LayeredTileMapLayer::DEBUG_VISIBILITY_MODE_FORCE_HIDE: show_collision = false; break; - case LayeredTileMapLayer::VISIBILITY_MODE_FORCE_SHOW: + case LayeredTileMapLayer::DEBUG_VISIBILITY_MODE_FORCE_SHOW: show_collision = true; break; } @@ -1024,7 +1003,6 @@ void LayeredTileMapLayer::_physics_draw_cell_debug(const RID &p_canvas_item, con void LayeredTileMapLayer::_navigation_update() { ERR_FAIL_NULL(Navigation2DServer::get_singleton()); Navigation2DServer *ns = Navigation2DServer::get_singleton(); - const Ref &tile_set = get_effective_tile_set(); // Check if we should cleanup everything. bool forced_cleanup = in_destructor || !enabled || !navigation_enabled || !is_inside_tree() || !tile_set.is_valid(); @@ -1056,13 +1034,13 @@ void LayeredTileMapLayer::_navigation_update() { // ----------- Navigation regions processing ----------- if (forced_cleanup) { // Clean everything. - for (HashMap::Element *kv = tile_map.front(); kv; kv = kv->next) { + for (HashMap::Element *kv = tile_map_layer_data.front(); kv; kv = kv->next) { _navigation_clear_cell(kv->value()); } } else { - if (_navigation_was_cleaned_up || dirty.flags[DIRTY_FLAGS_LAYER_GROUP_TILE_SET] || dirty.flags[DIRTY_FLAGS_LAYER_IN_TREE] || dirty.flags[DIRTY_FLAGS_LAYER_NAVIGATION_MAP]) { + if (_navigation_was_cleaned_up || dirty.flags[DIRTY_FLAGS_TILE_SET] || dirty.flags[DIRTY_FLAGS_LAYER_IN_TREE] || dirty.flags[DIRTY_FLAGS_LAYER_NAVIGATION_MAP]) { // Update all cells. - for (HashMap::Element *kv = tile_map.front(); kv; kv = kv->next) { + for (HashMap::Element *kv = tile_map_layer_data.front(); kv; kv = kv->next) { _navigation_update_cell(kv->value()); } } else { @@ -1080,11 +1058,10 @@ void LayeredTileMapLayer::_navigation_update() { } void LayeredTileMapLayer::_navigation_notification(int p_what) { - const Ref &tile_set = get_effective_tile_set(); if (p_what == NOTIFICATION_TRANSFORM_CHANGED) { if (tile_set.is_valid()) { Transform2D tilemap_xform = get_global_transform(); - for (HashMap::Element *kv = tile_map.front(); kv; kv = kv->next) { + for (HashMap::Element *kv = tile_map_layer_data.front(); kv; kv = kv->next) { const CellData &cell_data = kv->value(); // Update navigation regions transform. for (uint32_t i = 0; i < cell_data.navigation_regions.size(); ++i) { @@ -1117,7 +1094,6 @@ void LayeredTileMapLayer::_navigation_clear_cell(CellData &r_cell_data) { } void LayeredTileMapLayer::_navigation_update_cell(CellData &r_cell_data) { - const Ref &tile_set = get_effective_tile_set(); Navigation2DServer *ns = Navigation2DServer::get_singleton(); Transform2D gl_xform = get_global_transform(); RID navigation_map = navigation_map_override.is_valid() ? navigation_map_override : get_world_2d()->get_navigation_map(); @@ -1198,13 +1174,13 @@ void LayeredTileMapLayer::_navigation_draw_cell_debug(const RID &p_canvas_item, // Draw the debug collision shapes. bool show_navigation = false; switch (navigation_visibility_mode) { - case LayeredTileMapLayer::VISIBILITY_MODE_DEFAULT: + case LayeredTileMapLayer::DEBUG_VISIBILITY_MODE_DEFAULT: show_navigation = !Engine::get_singleton()->is_editor_hint() && get_tree()->is_debugging_navigation_hint(); break; - case LayeredTileMapLayer::VISIBILITY_MODE_FORCE_HIDE: + case LayeredTileMapLayer::DEBUG_VISIBILITY_MODE_FORCE_HIDE: show_navigation = false; break; - case LayeredTileMapLayer::VISIBILITY_MODE_FORCE_SHOW: + case LayeredTileMapLayer::DEBUG_VISIBILITY_MODE_FORCE_SHOW: show_navigation = true; break; } @@ -1217,8 +1193,6 @@ void LayeredTileMapLayer::_navigation_draw_cell_debug(const RID &p_canvas_item, return; } - const Ref &tile_set = get_effective_tile_set(); - RenderingServer *rs = RenderingServer::get_singleton(); const Navigation2DServer *ns2d = Navigation2DServer::get_singleton(); @@ -1304,20 +1278,18 @@ void LayeredTileMapLayer::_navigation_draw_cell_debug(const RID &p_canvas_item, /////////////////////////////// Scenes ////////////////////////////////////// void LayeredTileMapLayer::_scenes_update() { - const Ref &tile_set = get_effective_tile_set(); - // Check if we should cleanup everything. bool forced_cleanup = in_destructor || !enabled || !is_inside_tree() || !tile_set.is_valid(); if (forced_cleanup) { // Clean everything. - for (HashMap::Element *kv = tile_map.front(); kv; kv = kv->next) { + for (HashMap::Element *kv = tile_map_layer_data.front(); kv; kv = kv->next) { _scenes_clear_cell(kv->value()); } } else { - if (_scenes_was_cleaned_up || dirty.flags[DIRTY_FLAGS_LAYER_GROUP_TILE_SET] || dirty.flags[DIRTY_FLAGS_LAYER_IN_TREE]) { + if (_scenes_was_cleaned_up || dirty.flags[DIRTY_FLAGS_TILE_SET] || dirty.flags[DIRTY_FLAGS_LAYER_IN_TREE]) { // Update all cells. - for (HashMap::Element *kv = tile_map.front(); kv; kv = kv->next) { + for (HashMap::Element *kv = tile_map_layer_data.front(); kv; kv = kv->next) { _scenes_update_cell(kv->value()); } } else { @@ -1350,8 +1322,6 @@ void LayeredTileMapLayer::_scenes_clear_cell(CellData &r_cell_data) { } void LayeredTileMapLayer::_scenes_update_cell(CellData &r_cell_data) { - const Ref &tile_set = get_effective_tile_set(); - // Clear the scene in any case. _scenes_clear_cell(r_cell_data); @@ -1392,7 +1362,6 @@ void LayeredTileMapLayer::_scenes_update_cell(CellData &r_cell_data) { #ifdef DEBUG_ENABLED void LayeredTileMapLayer::_scenes_draw_cell_debug(const RID &p_canvas_item, const Vector2 &p_quadrant_pos, const CellData &r_cell_data) { - const Ref &tile_set = get_effective_tile_set(); ERR_FAIL_COND(!tile_set.is_valid()); if (!Engine::get_singleton()->is_editor_hint()) { @@ -1442,8 +1411,6 @@ void LayeredTileMapLayer::_scenes_draw_cell_debug(const RID &p_canvas_item, cons ///////////////////////////////////////////////////////////////////// void LayeredTileMapLayer::_build_runtime_update_tile_data() { - const Ref &tile_set = get_effective_tile_set(); - // Check if we should cleanup everything. bool forced_cleanup = in_destructor || !enabled || !tile_set.is_valid() || !is_visible_in_tree(); if (!forced_cleanup) { @@ -1453,14 +1420,14 @@ void LayeredTileMapLayer::_build_runtime_update_tile_data() { if (valid_runtime_update || valid_runtime_update_for_tilemap) { bool use_tilemap_for_runtime = valid_runtime_update_for_tilemap && !valid_runtime_update; - if (_runtime_update_tile_data_was_cleaned_up || dirty.flags[DIRTY_FLAGS_LAYER_GROUP_TILE_SET]) { + if (_runtime_update_tile_data_was_cleaned_up || dirty.flags[DIRTY_FLAGS_TILE_SET]) { _runtime_update_needs_all_cells_cleaned_up = true; - for (HashMap::Element *E = tile_map.front(); E; E = E->next) { + for (HashMap::Element *E = tile_map_layer_data.front(); E; E = E->next) { _build_runtime_update_tile_data_for_cell(E->value(), use_tilemap_for_runtime); } } else if (dirty.flags[DIRTY_FLAGS_LAYER_RUNTIME_UPDATE]) { - for (HashMap::Element *E = tile_map.front(); E; E = E->next) { + for (HashMap::Element *E = tile_map_layer_data.front(); E; E = E->next) { _build_runtime_update_tile_data_for_cell(E->value(), use_tilemap_for_runtime, true); } } else { @@ -1478,8 +1445,6 @@ void LayeredTileMapLayer::_build_runtime_update_tile_data() { } void LayeredTileMapLayer::_build_runtime_update_tile_data_for_cell(CellData &r_cell_data, bool p_use_tilemap_for_runtime, bool p_auto_add_to_dirty_list) { - const Ref &tile_set = get_effective_tile_set(); - LayeredTileMapCell &c = r_cell_data.cell; LayeredTileSetSource *source; if (tile_set->has_source(c.source_id)) { @@ -1527,7 +1492,7 @@ void LayeredTileMapLayer::_build_runtime_update_tile_data_for_cell(CellData &r_c void LayeredTileMapLayer::_clear_runtime_update_tile_data() { if (_runtime_update_needs_all_cells_cleaned_up) { - for (HashMap::Element *E = tile_map.front(); E; E = E->next) { + for (HashMap::Element *E = tile_map_layer_data.front(); E; E = E->next) { _clear_runtime_update_tile_data_for_cell(E->value()); } _runtime_update_needs_all_cells_cleaned_up = false; @@ -1548,8 +1513,6 @@ void LayeredTileMapLayer::_clear_runtime_update_tile_data_for_cell(CellData &r_c } LayeredTileSet::TerrainsPattern LayeredTileMapLayer::_get_best_terrain_pattern_for_constraints(int p_terrain_set, const Vector2i &p_position, const RBSet &p_constraints, LayeredTileSet::TerrainsPattern p_current_pattern) const { - Ref tile_set = get_effective_tile_set(); - if (!tile_set.is_valid()) { return LayeredTileSet::TerrainsPattern(); } @@ -1614,7 +1577,6 @@ LayeredTileSet::TerrainsPattern LayeredTileMapLayer::_get_best_terrain_pattern_f } RBSet LayeredTileMapLayer::_get_terrain_constraints_from_added_pattern(const Vector2i &p_position, int p_terrain_set, LayeredTileSet::TerrainsPattern p_terrains_pattern) const { - const Ref &tile_set = get_effective_tile_set(); if (!tile_set.is_valid()) { return RBSet(); } @@ -1635,7 +1597,6 @@ RBSet LayeredTileMapLayer::_get_terrain_constraints_from_adde } RBSet LayeredTileMapLayer::_get_terrain_constraints_from_painted_cells_list(const RBSet &p_painted, int p_terrain_set, bool p_ignore_empty_terrains) const { - const Ref &tile_set = get_effective_tile_set(); if (!tile_set.is_valid()) { return RBSet(); } @@ -1726,6 +1687,12 @@ RBSet LayeredTileMapLayer::_get_terrain_constraints_from_pain return constraints; } +void LayeredTileMapLayer::_tile_set_changed() { + dirty.flags[DIRTY_FLAGS_TILE_SET] = true; + _queue_internal_update(); + emit_signal(CoreStringNames::get_singleton()->changed); +} + void LayeredTileMapLayer::_renamed() { emit_signal(CoreStringNames::get_singleton()->changed); } @@ -1791,7 +1758,7 @@ void LayeredTileMapLayer::_internal_update() { // Remove cells that are empty after the cleanup. for (int i = 0; i < to_delete.size(); ++i) { - tile_map.erase(to_delete[i]); + tile_map_layer_data.erase(to_delete[i]); } // Clear the dirty cells list. @@ -1825,18 +1792,18 @@ void LayeredTileMapLayer::_notification(int p_what) { update_internals(); } break; - case LayeredTileMap::NOTIFICATION_ENTER_CANVAS: { + case NOTIFICATION_ENTER_CANVAS: { dirty.flags[DIRTY_FLAGS_LAYER_IN_CANVAS] = true; _queue_internal_update(); } break; - case LayeredTileMap::NOTIFICATION_EXIT_CANVAS: { + case NOTIFICATION_EXIT_CANVAS: { dirty.flags[DIRTY_FLAGS_LAYER_IN_CANVAS] = true; // Update immediately on exiting. update_internals(); } break; - case LayeredTileMap::NOTIFICATION_VISIBILITY_CHANGED: { + case NOTIFICATION_VISIBILITY_CHANGED: { dirty.flags[DIRTY_FLAGS_LAYER_VISIBILITY] = true; _queue_internal_update(); } break; @@ -1848,7 +1815,74 @@ void LayeredTileMapLayer::_notification(int p_what) { } void LayeredTileMapLayer::_bind_methods() { + // --- Cells manipulation --- + // Generic cells manipulations and access. ClassDB::bind_method(D_METHOD("set_cell", "coords", "source_id", "atlas_coords", "alternative_tile"), &LayeredTileMapLayer::set_cell, DEFVAL(LayeredTileSet::INVALID_SOURCE), DEFVAL(LayeredTileSetSource::INVALID_ATLAS_COORDS), DEFVAL(0)); + ClassDB::bind_method(D_METHOD("erase_cell", "coords"), &LayeredTileMapLayer::erase_cell); + ClassDB::bind_method(D_METHOD("fix_invalid_tiles"), &LayeredTileMapLayer::fix_invalid_tiles); + ClassDB::bind_method(D_METHOD("clear"), &LayeredTileMapLayer::clear); + + ClassDB::bind_method(D_METHOD("get_cell_source_id", "coords"), &LayeredTileMapLayer::get_cell_source_id); + ClassDB::bind_method(D_METHOD("get_cell_atlas_coords", "coords"), &LayeredTileMapLayer::get_cell_atlas_coords); + ClassDB::bind_method(D_METHOD("get_cell_alternative_tile", "coords"), &LayeredTileMapLayer::get_cell_alternative_tile); + ClassDB::bind_method(D_METHOD("get_cell_tile_data", "coords"), &LayeredTileMapLayer::get_cell_tile_data); + + ClassDB::bind_method(D_METHOD("get_used_cells"), &LayeredTileMapLayer::get_used_cells); + ClassDB::bind_method(D_METHOD("get_used_cells_by_id", "source_id", "atlas_coords", "alternative_tile"), &LayeredTileMapLayer::get_used_cells_by_id, DEFVAL(LayeredTileSet::INVALID_SOURCE), DEFVAL(LayeredTileSetSource::INVALID_ATLAS_COORDS), DEFVAL(LayeredTileSetSource::INVALID_TILE_ALTERNATIVE)); + ClassDB::bind_method(D_METHOD("get_used_rect"), &LayeredTileMapLayer::get_used_rect); + + // Patterns. + ClassDB::bind_method(D_METHOD("get_pattern", "coords_array"), &LayeredTileMapLayer::get_pattern); + ClassDB::bind_method(D_METHOD("set_pattern", "position", "pattern"), &LayeredTileMapLayer::set_pattern); + + // Terrains. + ClassDB::bind_method(D_METHOD("set_cells_terrain_connect", "cells", "terrain_set", "terrain", "ignore_empty_terrains"), &LayeredTileMapLayer::set_cells_terrain_connect, DEFVAL(true)); + ClassDB::bind_method(D_METHOD("set_cells_terrain_path", "path", "terrain_set", "terrain", "ignore_empty_terrains"), &LayeredTileMapLayer::set_cells_terrain_path, DEFVAL(true)); + + // --- Physics helpers --- + ClassDB::bind_method(D_METHOD("has_body_rid", "body"), &LayeredTileMapLayer::has_body_rid); + ClassDB::bind_method(D_METHOD("get_coords_for_body_rid", "body"), &LayeredTileMapLayer::get_coords_for_body_rid); + + // --- Runtime --- + ClassDB::bind_method(D_METHOD("update_internals"), &LayeredTileMapLayer::update_internals); + ClassDB::bind_method(D_METHOD("notify_runtime_tile_data_update"), &LayeredTileMapLayer::notify_runtime_tile_data_update, DEFVAL(-1)); + + // --- Shortcuts to methods defined in TileSet --- + ClassDB::bind_method(D_METHOD("map_pattern", "position_in_tilemap", "coords_in_pattern", "pattern"), &LayeredTileMapLayer::map_pattern); + ClassDB::bind_method(D_METHOD("get_surrounding_cells", "coords"), &LayeredTileMapLayer::get_surrounding_cells); + ClassDB::bind_method(D_METHOD("get_neighbor_cell", "coords", "neighbor"), &LayeredTileMapLayer::get_neighbor_cell); + ClassDB::bind_method(D_METHOD("map_to_local", "map_position"), &LayeredTileMapLayer::map_to_local); + ClassDB::bind_method(D_METHOD("local_to_map", "local_position"), &LayeredTileMapLayer::local_to_map); + + // --- Accessors --- + ClassDB::bind_method(D_METHOD("set_tile_map_data_from_array", "tile_map_layer_data"), &LayeredTileMapLayer::set_tile_map_data_from_array); + ClassDB::bind_method(D_METHOD("get_tile_map_data_as_array"), &LayeredTileMapLayer::get_tile_map_data_as_array); + + ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &LayeredTileMapLayer::set_enabled); + ClassDB::bind_method(D_METHOD("is_enabled"), &LayeredTileMapLayer::is_enabled); + + ClassDB::bind_method(D_METHOD("set_tile_set", "tile_set"), &LayeredTileMapLayer::set_tile_set); + ClassDB::bind_method(D_METHOD("get_tile_set"), &LayeredTileMapLayer::get_tile_set); + + ClassDB::bind_method(D_METHOD("set_y_sort_origin", "y_sort_origin"), &LayeredTileMapLayer::set_y_sort_origin); + ClassDB::bind_method(D_METHOD("get_y_sort_origin"), &LayeredTileMapLayer::get_y_sort_origin); + ClassDB::bind_method(D_METHOD("set_rendering_quadrant_size", "size"), &LayeredTileMapLayer::set_rendering_quadrant_size); + ClassDB::bind_method(D_METHOD("get_rendering_quadrant_size"), &LayeredTileMapLayer::get_rendering_quadrant_size); + + ClassDB::bind_method(D_METHOD("set_collision_enabled", "enabled"), &LayeredTileMapLayer::set_collision_enabled); + ClassDB::bind_method(D_METHOD("is_collision_enabled"), &LayeredTileMapLayer::is_collision_enabled); + ClassDB::bind_method(D_METHOD("set_use_kinematic_bodies", "use_kinematic_bodies"), &LayeredTileMapLayer::set_use_kinematic_bodies); + ClassDB::bind_method(D_METHOD("is_using_kinematic_bodies"), &LayeredTileMapLayer::is_using_kinematic_bodies); + ClassDB::bind_method(D_METHOD("set_collision_visibility_mode", "visibility_mode"), &LayeredTileMapLayer::set_collision_visibility_mode); + ClassDB::bind_method(D_METHOD("get_collision_visibility_mode"), &LayeredTileMapLayer::get_collision_visibility_mode); + + ClassDB::bind_method(D_METHOD("set_navigation_enabled", "enabled"), &LayeredTileMapLayer::set_navigation_enabled); + ClassDB::bind_method(D_METHOD("is_navigation_enabled"), &LayeredTileMapLayer::is_navigation_enabled); + ClassDB::bind_method(D_METHOD("set_navigation_map", "map"), &LayeredTileMapLayer::set_navigation_map); + ClassDB::bind_method(D_METHOD("get_navigation_map"), &LayeredTileMapLayer::get_navigation_map); + ClassDB::bind_method(D_METHOD("set_navigation_visibility_mode", "show_navigation"), &LayeredTileMapLayer::set_navigation_visibility_mode); + ClassDB::bind_method(D_METHOD("get_navigation_visibility_mode"), &LayeredTileMapLayer::get_navigation_visibility_mode); + BIND_VMETHOD(MethodInfo(Variant::BOOL, "_use_tile_data_runtime_update", PropertyInfo(Variant::VECTOR2I, "coords"))); @@ -1862,13 +1896,35 @@ void LayeredTileMapLayer::_bind_methods() { ClassDB::bind_method(D_METHOD("_deferred_internal_update"), &LayeredTileMapLayer::_deferred_internal_update); ClassDB::bind_method(D_METHOD("_renamed"), &LayeredTileMapLayer::_renamed); + ClassDB::bind_method(D_METHOD("_tile_set_changed"), &LayeredTileMapLayer::_tile_set_changed); + + ADD_PROPERTY(PropertyInfo(Variant::POOL_BYTE_ARRAY, "tile_map_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_tile_map_data_from_array", "get_tile_map_data_as_array"); + + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "tile_set", PROPERTY_HINT_RESOURCE_TYPE, "LayeredTileSet"), "set_tile_set", "get_tile_set"); + ADD_GROUP("Rendering", ""); + ADD_PROPERTY(PropertyInfo(Variant::INT, "y_sort_origin"), "set_y_sort_origin", "get_y_sort_origin"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "rendering_quadrant_size"), "set_rendering_quadrant_size", "get_rendering_quadrant_size"); + ADD_GROUP("Physics", ""); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collision_enabled"), "set_collision_enabled", "is_collision_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_kinematic_bodies"), "set_use_kinematic_bodies", "is_using_kinematic_bodies"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_visibility_mode", PROPERTY_HINT_ENUM, "Default,Force Show,Force Hide"), "set_collision_visibility_mode", "get_collision_visibility_mode"); + ADD_GROUP("Navigation", ""); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "navigation_enabled"), "set_navigation_enabled", "is_navigation_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_visibility_mode", PROPERTY_HINT_ENUM, "Default,Force Show,Force Hide"), "set_navigation_visibility_mode", "get_navigation_visibility_mode"); ADD_SIGNAL(MethodInfo(CoreStringNames::get_singleton()->changed)); - //VertexLights2D +//VertexLights2D #ifdef MODULE_VERTEX_LIGHTS_2D_ENABLED ClassDB::bind_method(D_METHOD("_on_vertex_lights_map_changed"), &LayeredTileMapLayer::_on_vertex_lights_map_changed); #endif + + ADD_PROPERTY_DEFAULT("tile_map_data_format", LayeredTileMapLayerDataFormat::LAYERED_TILE_MAP_LAYER_DATA_FORMAT_0); + + BIND_ENUM_CONSTANT(DEBUG_VISIBILITY_MODE_DEFAULT); + BIND_ENUM_CONSTANT(DEBUG_VISIBILITY_MODE_FORCE_HIDE); + BIND_ENUM_CONSTANT(DEBUG_VISIBILITY_MODE_FORCE_SHOW); } void LayeredTileMapLayer::set_as_tile_map_internal_node(int p_index) { @@ -1885,7 +1941,6 @@ void LayeredTileMapLayer::set_as_tile_map_internal_node(int p_index) { } Rect2 LayeredTileMapLayer::get_rect(bool &r_changed) const { - const Ref &tile_set = get_effective_tile_set(); if (tile_set.is_null()) { r_changed = rect_cache != Rect2(); return Rect2(); @@ -1899,7 +1954,7 @@ Rect2 LayeredTileMapLayer::get_rect(bool &r_changed) const { Rect2 r_total; bool first = true; - for (const HashMap::Element *E = tile_map.front(); E; E = E->next) { + for (const HashMap::Element *E = tile_map_layer_data.front(); E; E = E->next) { Rect2 r; r.position = tile_set->map_to_local(E->key()); r.size = Size2(); @@ -1921,7 +1976,6 @@ Rect2 LayeredTileMapLayer::get_rect(bool &r_changed) const { } HashMap LayeredTileMapLayer::terrain_fill_constraints(const Vector &p_to_replace, int p_terrain_set, const RBSet &p_constraints) const { - const Ref &tile_set = get_effective_tile_set(); if (!tile_set.is_valid()) { return HashMap(); } @@ -1971,7 +2025,6 @@ HashMap LayeredTileMapLayer::terrain_ HashMap LayeredTileMapLayer::terrain_fill_connect(const Vector &p_coords_array, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains) const { HashMap output; - const Ref &tile_set = get_effective_tile_set(); ERR_FAIL_COND_V(!tile_set.is_valid(), output); ERR_FAIL_INDEX_V(p_terrain_set, tile_set->get_terrain_sets_count(), output); @@ -2089,7 +2142,6 @@ HashMap LayeredTileMapLayer::terrain_ HashMap LayeredTileMapLayer::terrain_fill_path(const Vector &p_coords_array, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains) const { HashMap output; - const Ref &tile_set = get_effective_tile_set(); ERR_FAIL_COND_V(!tile_set.is_valid(), output); ERR_FAIL_INDEX_V(p_terrain_set, tile_set->get_terrain_sets_count(), output); @@ -2170,7 +2222,6 @@ HashMap LayeredTileMapLayer::terrain_ HashMap LayeredTileMapLayer::terrain_fill_pattern(const Vector &p_coords_array, int p_terrain_set, LayeredTileSet::TerrainsPattern p_terrains_pattern, bool p_ignore_empty_terrains) const { HashMap output; - const Ref &tile_set = get_effective_tile_set(); ERR_FAIL_COND_V(!tile_set.is_valid(), output); ERR_FAIL_INDEX_V(p_terrain_set, tile_set->get_terrain_sets_count(), output); @@ -2230,154 +2281,21 @@ HashMap LayeredTileMapLayer::terrain_ return output; } -LayeredTileMapCell LayeredTileMapLayer::get_cell(const Vector2i &p_coords, bool p_use_proxies) const { - if (!tile_map.has(p_coords)) { +LayeredTileMapCell LayeredTileMapLayer::get_cell(const Vector2i &p_coords) const { + const HashMap::Element* E = tile_map_layer_data.find(p_coords); + + if (!E) { return LayeredTileMapCell(); } else { - LayeredTileMapCell c = tile_map.find(p_coords)->value().cell; - const Ref &tile_set = get_effective_tile_set(); - if (p_use_proxies && tile_set.is_valid()) { - Array proxyed = tile_set->map_tile_proxy(c.source_id, c.get_atlas_coords(), c.alternative_tile); - c.source_id = proxyed[0]; - c.set_atlas_coords(proxyed[1]); - c.alternative_tile = proxyed[2]; - } - return c; + return E->value().cell; } } -void LayeredTileMapLayer::set_tile_data(LayeredTileMapDataFormat p_format, const Vector &p_data) { - ERR_FAIL_COND(p_format > LayeredTileMapDataFormat::FORMAT_3); - - // Set data for a given tile from raw data. - - int c = p_data.size(); - const int *r = p_data.ptr(); - - int offset = (p_format >= LayeredTileMapDataFormat::FORMAT_2) ? 3 : 2; - ERR_FAIL_COND_MSG(c % offset != 0, vformat("Corrupted tile data. Got size: %s. Expected modulo: %s", offset)); - - clear(); - -#ifdef DISABLE_DEPRECATED - ERR_FAIL_COND_MSG(p_format != LayeredTileMapDataFormat::FORMAT_3, vformat("Cannot handle deprecated LayeredTileMapLayer data format version %d. This Godot version was compiled with no support for deprecated data.", p_format)); -#endif - - 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 < ((p_format >= LayeredTileMapDataFormat::FORMAT_2) ? 12 : 8); j++) { - local[j] = ptr[j]; - } - -#ifdef BIG_ENDIAN_ENABLED - - SWAP(local[0], local[3]); - SWAP(local[1], local[2]); - SWAP(local[4], local[7]); - SWAP(local[5], local[6]); - //TODO: ask someone to check this... - if (FORMAT >= FORMAT_2) { - SWAP(local[8], local[11]); - SWAP(local[9], local[10]); - } -#endif - // Extracts position in LayeredTileMap. - int16_t x = decode_uint16(&local[0]); - int16_t y = decode_uint16(&local[2]); - - if (p_format == LayeredTileMapDataFormat::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_uint16(&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 { -#ifndef DISABLE_DEPRECATED - // Previous decated format. - - uint32_t v = decode_uint32(&local[4]); - // Extract the transform flags that used to be in the tilemap. - bool flip_h = v & (1UL << 29); - bool flip_v = v & (1UL << 30); - bool transpose = v & (1UL << 31); - v &= (1UL << 29) - 1; - - // Extract autotile/atlas coords. - int16_t coord_x = 0; - int16_t coord_y = 0; - if (p_format == LayeredTileMapDataFormat::FORMAT_2) { - coord_x = decode_uint16(&local[8]); - coord_y = decode_uint16(&local[10]); - } - - Ref tile_set = get_effective_tile_set(); - if (tile_set.is_valid()) { - Array a = tile_set->compatibility_tilemap_map(v, Vector2i(coord_x, coord_y), flip_h, flip_v, transpose); - if (a.size() == 3) { - set_cell(Vector2i(x, y), a[0], a[1], a[2]); - } else { - ERR_PRINT(vformat("No valid tile in Tileset for: tile:%s coords:%s flip_h:%s flip_v:%s transpose:%s", v, Vector2i(coord_x, coord_y), flip_h, flip_v, transpose)); - } - } else { - int compatibility_alternative_tile = ((int)flip_h) + ((int)flip_v << 1) + ((int)transpose << 2); - set_cell(Vector2i(x, y), v, Vector2i(coord_x, coord_y), compatibility_alternative_tile); - } -#endif - } - } -} - -Vector LayeredTileMapLayer::get_tile_data() const { - // Export tile data to raw format. - Vector tile_data; - tile_data.resize(tile_map.size() * 3); - int *w = tile_data.ptrw(); - - // Save in highest format. - - int idx = 0; - for (const HashMap::Element *E = tile_map.front(); E; E = E->next) { - uint8_t *ptr = (uint8_t *)&w[idx]; - encode_uint16((int16_t)(E->key().x), &ptr[0]); - encode_uint16((int16_t)(E->key().y), &ptr[2]); - encode_uint16(E->value().cell.source_id, &ptr[4]); - encode_uint16(E->value().cell.coord_x, &ptr[6]); - encode_uint16(E->value().cell.coord_y, &ptr[8]); - encode_uint16(E->value().cell.alternative_tile, &ptr[10]); - idx += 3; - } - - return tile_data; -} - -void LayeredTileMapLayer::notify_tile_map_layer_group_change(DirtyFlags p_what) { - if (p_what == DIRTY_FLAGS_LAYER_GROUP_SELECTED_LAYERS || - p_what == DIRTY_FLAGS_LAYER_GROUP_HIGHLIGHT_SELECTED || - p_what == DIRTY_FLAGS_LAYER_GROUP_TILE_SET) { - emit_signal(CoreStringNames::get_singleton()->changed); - } - - dirty.flags[p_what] = true; - _queue_internal_update(); -} - -void LayeredTileMapLayer::update_internals() { - pending_update = true; - _deferred_internal_update(); -} - -void LayeredTileMapLayer::notify_runtime_tile_data_update() { - dirty.flags[LayeredTileMapLayer::DIRTY_FLAGS_LAYER_RUNTIME_UPDATE] = true; - _queue_internal_update(); - emit_signal(CoreStringNames::get_singleton()->changed); -} - -void LayeredTileMapLayer::set_cell(const Vector2i &p_coords, int p_source_id, const Vector2i p_atlas_coords, int p_alternative_tile) { +void LayeredTileMapLayer::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); - HashMap::Element *E = tile_map.find(pk); + HashMap::Element *E = tile_map_layer_data.find(pk); int source_id = p_source_id; Vector2i atlas_coords = p_atlas_coords; @@ -2398,7 +2316,7 @@ void LayeredTileMapLayer::set_cell(const Vector2i &p_coords, int p_source_id, co // Insert a new cell in the tile map. CellData new_cell_data; new_cell_data.coords = pk; - E = tile_map.insert(pk, new_cell_data); + E = tile_map_layer_data.insert(pk, new_cell_data); } else { if (E->value().cell.source_id == source_id && E->value().cell.get_atlas_coords() == atlas_coords && E->value().cell.alternative_tile == alternative_tile) { return; // Nothing changed. @@ -2423,70 +2341,20 @@ void LayeredTileMapLayer::erase_cell(const Vector2i &p_coords) { set_cell(p_coords, LayeredTileSet::INVALID_SOURCE, LayeredTileSetSource::INVALID_ATLAS_COORDS, LayeredTileSetSource::INVALID_TILE_ALTERNATIVE); } -int LayeredTileMapLayer::get_cell_source_id(const Vector2i &p_coords, bool p_use_proxies) const { - // Get a cell source id from position. - const HashMap::Element *E = tile_map.find(p_coords); +void LayeredTileMapLayer::fix_invalid_tiles() { + ERR_FAIL_COND_MSG(tile_set.is_null(), "Cannot call fix_invalid_tiles() on a LayeredTileMap without a valid LayeredTileSet."); - if (!E) { - return LayeredTileSet::INVALID_SOURCE; + RBSet coords; + for (const HashMap::Element *E = tile_map.front(); E; E = E->next) { + LayeredTileSetSource *source = *tile_set->get_source(E->value().cell.source_id); + if (!source || !source->has_tile(E->value().cell.get_atlas_coords()) || !source->has_alternative_tile(E->value().cell.get_atlas_coords(), E->value().cell.alternative_tile)) { + coords.insert(E->key()); + } } - const Ref &tile_set = get_effective_tile_set(); - if (p_use_proxies && tile_set.is_valid()) { - Array proxyed = tile_set->map_tile_proxy(E->value().cell.source_id, E->value().cell.get_atlas_coords(), E->value().cell.alternative_tile); - return proxyed[0]; + for (RBSet::Element *E = coords.front(); E; E = E->next()) { + set_cell(E->get(), LayeredTileSet::INVALID_SOURCE, LayeredTileSetSource::INVALID_ATLAS_COORDS, LayeredTileSetSource::INVALID_TILE_ALTERNATIVE); } - - return E->value().cell.source_id; -} - -Vector2i LayeredTileMapLayer::get_cell_atlas_coords(const Vector2i &p_coords, bool p_use_proxies) const { - // Get a cell source id from position. - const HashMap::Element *E = tile_map.find(p_coords); - - if (!E) { - return LayeredTileSetSource::INVALID_ATLAS_COORDS; - } - - const Ref &tile_set = get_effective_tile_set(); - if (p_use_proxies && tile_set.is_valid()) { - Array proxyed = tile_set->map_tile_proxy(E->value().cell.source_id, E->value().cell.get_atlas_coords(), E->value().cell.alternative_tile); - return proxyed[1]; - } - - return E->value().cell.get_atlas_coords(); -} - -int LayeredTileMapLayer::get_cell_alternative_tile(const Vector2i &p_coords, bool p_use_proxies) const { - // Get a cell source id from position. - const HashMap::Element *E = tile_map.find(p_coords); - - if (!E) { - return LayeredTileSetSource::INVALID_TILE_ALTERNATIVE; - } - - const Ref &tile_set = get_effective_tile_set(); - if (p_use_proxies && tile_set.is_valid()) { - Array proxyed = tile_set->map_tile_proxy(E->value().cell.source_id, E->value().cell.get_atlas_coords(), E->value().cell.alternative_tile); - return proxyed[2]; - } - - return E->value().cell.alternative_tile; -} - -LayeredTileData *LayeredTileMapLayer::get_cell_tile_data(const Vector2i &p_coords, bool p_use_proxies) const { - int source_id = get_cell_source_id(p_coords, p_use_proxies); - if (source_id == LayeredTileSet::INVALID_SOURCE) { - return nullptr; - } - - const Ref &tile_set = get_effective_tile_set(); - Ref source = tile_set->get_source(source_id); - if (source.is_valid()) { - return source->get_tile_data(get_cell_atlas_coords(p_coords, p_use_proxies), get_cell_alternative_tile(p_coords, p_use_proxies)); - } - - return nullptr; } void LayeredTileMapLayer::clear() { @@ -2497,8 +2365,116 @@ void LayeredTileMapLayer::clear() { used_rect_cache_dirty = true; } +int LayeredTileMapLayer::get_cell_source_id(const Vector2i &p_coords) const { + // Get a cell source id from position. + const HashMap::Element *E = tile_map_layer_data.find(p_coords); + + if (!E) { + return LayeredTileSet::INVALID_SOURCE; + } + + return E->value().cell.source_id; +} + +Vector2i LayeredTileMapLayer::get_cell_atlas_coords(const Vector2i &p_coords) const { + // Get a cell source id from position. + const HashMap::Element *E = tile_map_layer_data.find(p_coords); + + if (!E) { + return LayeredTileSetSource::INVALID_ATLAS_COORDS; + } + + return E->value().cell.get_atlas_coords(); +} + +int LayeredTileMapLayer::get_cell_alternative_tile(const Vector2i &p_coords) const { + // Get a cell source id from position. + const HashMap::Element *E = tile_map.find(p_coords); + + if (!E) { + return LayeredTileSetSource::INVALID_TILE_ALTERNATIVE; + } + + return E->value().cell.alternative_tile; +} + +LayeredTileData *LayeredTileMapLayer::get_cell_tile_data(const Vector2i &p_coords) const { + int source_id = get_cell_source_id(p_coords); + if (source_id == LayeredTileSet::INVALID_SOURCE) { + return nullptr; + } + + Ref source = tile_set->get_source(source_id); + if (source.is_valid()) { + return source->get_tile_data(get_cell_atlas_coords(p_coords), get_cell_alternative_tile(p_coords)); + } + + return nullptr; +} + +PoolVector2iArray LayeredTileMapLayer::get_used_cells() const { + // Returns the cells used in the tilemap. + PoolVector2iArray a; + for (const HashMap::Element *E = tile_map.front(); E; E = E->next) { + const LayeredTileMapCell &c = E->value().cell; + if (c.source_id == LayeredTileSet::INVALID_SOURCE) { + continue; + } + a.push_back(E->key()); + } + + return a; +} + +PoolVector2iArray LayeredTileMapLayer::get_used_cells_by_id(int p_source_id, const Vector2i &p_atlas_coords, int p_alternative_tile) const { + // Returns the cells used in the tilemap. + PoolVector2iArray a; + for (const HashMap::Element *E = tile_map.front(); E; E = E->next) { + const LayeredTileMapCell &c = E->value().cell; + if (c.source_id == LayeredTileSet::INVALID_SOURCE) { + continue; + } + if ((p_source_id == LayeredTileSet::INVALID_SOURCE || p_source_id == c.source_id) && + (p_atlas_coords == LayeredTileSetSource::INVALID_ATLAS_COORDS || p_atlas_coords == c.get_atlas_coords()) && + (p_alternative_tile == LayeredTileSetSource::INVALID_TILE_ALTERNATIVE || p_alternative_tile == c.alternative_tile)) { + a.push_back(E->key()); + } + } + + return a; +} + +Rect2i LayeredTileMapLayer::get_used_rect() const { + // Return the rect of the currently used area. + if (used_rect_cache_dirty) { + used_rect_cache = Rect2i(); + + bool first = true; + for (const HashMap::Element *E = tile_map.front(); E; E = E->next) { + const LayeredTileMapCell &c = E->value().cell; + if (c.source_id == LayeredTileSet::INVALID_SOURCE) { + continue; + } + if (first) { + used_rect_cache = Rect2i(E->key(), Size2i()); + first = false; + } else { + used_rect_cache.expand_to(E->key()); + } + } + if (!first) { + // Only if we have at least one cell. + // The cache expands to top-left coordinate, so we add one full tile. + used_rect_cache.size += Vector2i(1, 1); + } + used_rect_cache_dirty = false; + } + + return used_rect_cache; +} + + Ref LayeredTileMapLayer::get_pattern(PoolVector2iArray p_coords_array) { - const Ref &tile_set = get_effective_tile_set(); ERR_FAIL_COND_V(!tile_set.is_valid(), nullptr); Ref output; @@ -2552,7 +2528,6 @@ Ref LayeredTileMapLayer::get_pattern(PoolVector2iArray p_ } void LayeredTileMapLayer::set_pattern(const Vector2i &p_position, const Ref p_pattern) { - const Ref &tile_set = get_effective_tile_set(); ERR_FAIL_COND(tile_set.is_null()); ERR_FAIL_COND(p_pattern.is_null()); @@ -2564,7 +2539,6 @@ void LayeredTileMapLayer::set_pattern(const Vector2i &p_position, const Ref tile_set = get_effective_tile_set(); ERR_FAIL_COND(!tile_set.is_valid()); ERR_FAIL_INDEX(p_terrain_set, tile_set->get_terrain_sets_count()); @@ -2607,7 +2581,6 @@ void LayeredTileMapLayer::set_cells_terrain_connect(PoolVector2iArray p_cells, i } void LayeredTileMapLayer::set_cells_terrain_path(PoolVector2iArray p_path, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains) { - Ref tile_set = get_effective_tile_set(); ERR_FAIL_COND(!tile_set.is_valid()); ERR_FAIL_INDEX(p_terrain_set, tile_set->get_terrain_sets_count()); @@ -2649,65 +2622,50 @@ void LayeredTileMapLayer::set_cells_terrain_path(PoolVector2iArray p_path, int p } } -PoolVector2iArray LayeredTileMapLayer::get_used_cells() const { - // Returns the cells used in the tilemap. - PoolVector2iArray a; - for (const HashMap::Element *E = tile_map.front(); E; E = E->next) { - const LayeredTileMapCell &c = E->value().cell; - if (c.source_id == LayeredTileSet::INVALID_SOURCE) { - continue; - } - a.push_back(E->key()); - } - - return a; +bool LayeredTileMapLayer::has_body_rid(RID p_physics_body) const { + return bodies_coords.has(p_physics_body); } -PoolVector2iArray LayeredTileMapLayer::get_used_cells_by_id(int p_source_id, const Vector2i p_atlas_coords, int p_alternative_tile) const { - // Returns the cells used in the tilemap. - PoolVector2iArray a; - for (const HashMap::Element *E = tile_map.front(); E; E = E->next) { - const LayeredTileMapCell &c = E->value().cell; - if (c.source_id == LayeredTileSet::INVALID_SOURCE) { - continue; - } - if ((p_source_id == LayeredTileSet::INVALID_SOURCE || p_source_id == c.source_id) && - (p_atlas_coords == LayeredTileSetSource::INVALID_ATLAS_COORDS || p_atlas_coords == c.get_atlas_coords()) && - (p_alternative_tile == LayeredTileSetSource::INVALID_TILE_ALTERNATIVE || p_alternative_tile == c.alternative_tile)) { - a.push_back(E->key()); - } - } - - return a; +Vector2i LayeredTileMapLayer::get_coords_for_body_rid(RID p_physics_body) const { + const Vector2i *found = bodies_coords.getptr(p_physics_body); + ERR_FAIL_NULL_V(found, Vector2i()); + return *found; } -Rect2i LayeredTileMapLayer::get_used_rect() const { - // Return the rect of the currently used area. - if (used_rect_cache_dirty) { - used_rect_cache = Rect2i(); +void LayeredTileMapLayer::update_internals() { + pending_update = true; + _deferred_internal_update(); +} - bool first = true; - for (const HashMap::Element *E = tile_map.front(); E; E = E->next) { - const LayeredTileMapCell &c = E->value().cell; - if (c.source_id == LayeredTileSet::INVALID_SOURCE) { - continue; - } - if (first) { - used_rect_cache = Rect2i(E->key().x, E->key().y, 0, 0); - first = false; - } else { - used_rect_cache.expand_to(E->key()); - } - } - if (!first) { - // Only if we have at least one cell. - // The cache expands to top-left coordinate, so we add one full tile. - used_rect_cache.size += Vector2i(1, 1); - } - used_rect_cache_dirty = false; - } +void LayeredTileMapLayer::notify_runtime_tile_data_update() { + dirty.flags[LayeredTileMapLayer::DIRTY_FLAGS_LAYER_RUNTIME_UPDATE] = true; + _queue_internal_update(); + emit_signal(CoreStringNames::get_singleton()->changed); +} - return used_rect_cache; +Vector2i LayeredTileMapLayer::map_pattern(const Vector2i &p_position_in_tilemap, const Vector2i &p_coords_in_pattern, Ref p_pattern) { + ERR_FAIL_COND_V(tile_set.is_null(), Vector2i()); + return tile_set->map_pattern(p_position_in_tilemap, p_coords_in_pattern, p_pattern); +} + +PoolVector2iArray LayeredTileMapLayer::get_surrounding_cells(const Vector2i &p_coords) { + ERR_FAIL_COND_V(tile_set.is_null(), PoolVector2iArray()); + return tile_set->get_surrounding_cells(p_coords); +} + +Vector2i LayeredTileMapLayer::get_neighbor_cell(const Vector2i &p_coords, LayeredTileSet::CellNeighbor p_cell_neighbor) const { + ERR_FAIL_COND_V(tile_set.is_null(), Vector2i()); + return tile_set->get_neighbor_cell(p_coords, p_cell_neighbor); +} + +Vector2 LayeredTileMapLayer::map_to_local(const Vector2i &p_pos) const { + ERR_FAIL_COND_V(tile_set.is_null(), Vector2()); + return tile_set->map_to_local(p_pos); +} + +Vector2i LayeredTileMapLayer::local_to_map(const Vector2 &p_pos) const { + ERR_FAIL_COND_V(tile_set.is_null(), Vector2i()); + return tile_set->local_to_map(p_pos); } void LayeredTileMapLayer::set_enabled(bool p_enabled) { @@ -2718,16 +2676,127 @@ void LayeredTileMapLayer::set_enabled(bool p_enabled) { dirty.flags[DIRTY_FLAGS_LAYER_ENABLED] = true; _queue_internal_update(); emit_signal(CoreStringNames::get_singleton()->changed); - - if (tile_map_node) { - tile_map_node->update_configuration_warning(); - } } bool LayeredTileMapLayer::is_enabled() const { return enabled; } +void LayeredTileMapLayer::set_tile_set(const Ref &p_tile_set) { + if (p_tile_set == tile_set) { + return; + } + + dirty.flags[DIRTY_FLAGS_TILE_SET] = true; + _queue_internal_update(); + + // Set the TileSet, registering to its changes. + if (tile_set.is_valid()) { + tile_set->disconnect("changed", this, "_tile_set_changed"); + } + + tile_set = p_tile_set; + + if (tile_set.is_valid()) { + tile_set->connect("changed", this, "_tile_set_changed"); + } + + emit_signal(CoreStringNames::get_singleton()->changed); + + // Trigger updates for TileSet's read-only status. + property_list_changed_notify(); +} + +Ref LayeredTileMapLayer::get_tile_set() const { + return tile_set; +} + +void LayeredTileMapLayer::set_highlight_mode(HighlightMode p_highlight_mode) { + if (p_highlight_mode == highlight_mode) { + return; + } + highlight_mode = p_highlight_mode; + _queue_internal_update(); +} + +LayeredTileMapLayer::HighlightMode LayeredTileMapLayer::get_highlight_mode() const { + return highlight_mode; +} + +void LayeredTileMapLayer::set_tile_map_data_from_array(const Vector &p_data) { + const int cell_data_struct_size = 12; + + int size = p_data.size(); + const uint8_t *ptr = p_data.ptr(); + + // Index in the array. + int index = 0; + + // First extract the data version. + ERR_FAIL_COND_MSG(size < 2, "Corrupted tile map data: not enough bytes."); + uint16_t format = decode_uint16(&ptr[index]); + index += 2; + ERR_FAIL_COND_MSG(format >= LayeredTileMapLayerDataFormat::LAYERED_TILE_MAP_LAYER_DATA_FORMAT_MAX, vformat("Unsupported tile map data format: %s. Expected format ID lower or equal to: %s", format, LayeredTileMapLayerDataFormat::LAYERED_TILE_MAP_LAYER_DATA_FORMAT_MAX - 1)); + + // Clear the TileMap. + clear(); + + while (index < size) { + ERR_FAIL_COND_MSG(index + cell_data_struct_size > size, vformat("Corrupted tile map data: tiles might be missing.")); + + // Get a pointer at the start of the cell data. + const uint8_t *cell_data_ptr = &ptr[index]; + + // Extracts position in TileMap. + int16_t x = decode_uint16(&cell_data_ptr[0]); + int16_t y = decode_uint16(&cell_data_ptr[2]); + + // Extracts the tile identifiers. + uint16_t source_id = decode_uint16(&cell_data_ptr[4]); + uint16_t atlas_coords_x = decode_uint16(&cell_data_ptr[6]); + uint16_t atlas_coords_y = decode_uint16(&cell_data_ptr[8]); + uint16_t alternative_tile = decode_uint16(&cell_data_ptr[10]); + + set_cell(Vector2i(x, y), source_id, Vector2i(atlas_coords_x, atlas_coords_y), alternative_tile); + index += cell_data_struct_size; + } +} + +Vector LayeredTileMapLayer::get_tile_map_data_as_array() const { + const int cell_data_struct_size = 12; + + Vector tile_map_data_array; + tile_map_data_array.resize(2 + tile_map_layer_data.size() * cell_data_struct_size); + uint8_t *ptr = tile_map_data_array.ptrw(); + + // Index in the array. + int index = 0; + + // Save the version. + encode_uint16(LayeredTileMapLayerDataFormat::LAYERED_TILE_MAP_LAYER_DATA_FORMAT_MAX - 1, &ptr[index]); + index += 2; + + // Save in highest format. + for (const HashMap::Element *E = tile_map_layer_data.front(); E; E = E->next) { + // Get a pointer at the start of the cell data. + uint8_t *cell_data_ptr = (uint8_t *)&ptr[index]; + + // Store position in TileMap. + encode_uint16((int16_t)(E->key().x), &cell_data_ptr[0]); + encode_uint16((int16_t)(E->key().y), &cell_data_ptr[2]); + + // Store the tile identifiers. + encode_uint16(E->value().cell.source_id, &cell_data_ptr[4]); + encode_uint16(E->value().cell.coord_x, &cell_data_ptr[6]); + encode_uint16(E->value().cell.coord_y, &cell_data_ptr[8]); + encode_uint16(E->value().cell.alternative_tile, &cell_data_ptr[10]); + + index += cell_data_struct_size; + } + + return tile_map_data_array; +} + void LayeredTileMapLayer::set_self_modulate(const Color &p_self_modulate) { if (get_self_modulate() == p_self_modulate) { return; @@ -2749,9 +2818,6 @@ void LayeredTileMapLayer::set_y_sort_enabled(bool p_y_sort_enabled) { _queue_internal_update(); emit_signal(CoreStringNames::get_singleton()->changed); - if (tile_map_node) { - tile_map_node->update_configuration_warning(); - } _update_notify_local_transform(); } @@ -2779,10 +2845,6 @@ void LayeredTileMapLayer::set_z_index(int p_z_index) { dirty.flags[DIRTY_FLAGS_LAYER_Z_INDEX] = true; _queue_internal_update(); emit_signal(CoreStringNames::get_singleton()->changed); - - if (tile_map_node) { - tile_map_node->update_configuration_warning(); - } } void LayeredTileMapLayer::set_light_mask(int p_light_mask) { @@ -2811,6 +2873,20 @@ int LayeredTileMapLayer::get_rendering_quadrant_size() const { return rendering_quadrant_size; } +void LayeredTileMapLayer::set_collision_enabled(bool p_enabled) { + if (collision_enabled == p_enabled) { + return; + } + collision_enabled = p_enabled; + dirty.flags[DIRTY_FLAGS_LAYER_COLLISION_ENABLED] = true; + _queue_internal_update(); + emit_signal(CoreStringNames::get_singleton()->changed); +} + +bool LayeredTileMapLayer::is_collision_enabled() const { + return collision_enabled; +} + void LayeredTileMapLayer::set_use_kinematic_bodies(bool p_use_kinematic_bodies) { use_kinematic_bodies = p_use_kinematic_bodies; dirty.flags[DIRTY_FLAGS_LAYER_USE_KINEMATIC_BODIES] = p_use_kinematic_bodies; @@ -2822,7 +2898,7 @@ bool LayeredTileMapLayer::is_using_kinematic_bodies() const { return use_kinematic_bodies; } -void LayeredTileMapLayer::set_collision_visibility_mode(LayeredTileMapLayer::VisibilityMode p_show_collision) { +void LayeredTileMapLayer::set_collision_visibility_mode(LayeredTileMapLayer::DebugVisibilityMode p_show_collision) { if (collision_visibility_mode == p_show_collision) { return; } @@ -2832,7 +2908,7 @@ void LayeredTileMapLayer::set_collision_visibility_mode(LayeredTileMapLayer::Vis emit_signal(CoreStringNames::get_singleton()->changed); } -LayeredTileMapLayer::VisibilityMode LayeredTileMapLayer::get_collision_visibility_mode() const { +LayeredTileMapLayer::DebugVisibilityMode LayeredTileMapLayer::get_collision_visibility_mode() const { return collision_visibility_mode; } @@ -2869,7 +2945,7 @@ RID LayeredTileMapLayer::get_navigation_map() const { return RID(); } -void LayeredTileMapLayer::set_navigation_visibility_mode(LayeredTileMapLayer::VisibilityMode p_show_navigation) { +void LayeredTileMapLayer::set_navigation_visibility_mode(LayeredTileMapLayer::DebugVisibilityMode p_show_navigation) { if (navigation_visibility_mode == p_show_navigation) { return; } @@ -2879,7 +2955,7 @@ void LayeredTileMapLayer::set_navigation_visibility_mode(LayeredTileMapLayer::Vi emit_signal(CoreStringNames::get_singleton()->changed); } -LayeredTileMapLayer::VisibilityMode LayeredTileMapLayer::get_navigation_visibility_mode() const { +LayeredTileMapLayer::DebugVisibilityMode LayeredTileMapLayer::get_navigation_visibility_mode() const { return navigation_visibility_mode; } @@ -2937,40 +3013,6 @@ real_t LayeredTileMapLayer::get_rao_strength() const { #endif -void LayeredTileMapLayer::fix_invalid_tiles() { - Ref tileset = get_effective_tile_set(); - ERR_FAIL_COND_MSG(tileset.is_null(), "Cannot call fix_invalid_tiles() on a LayeredTileMap without a valid LayeredTileSet."); - - RBSet coords; - for (const HashMap::Element *E = tile_map.front(); E; E = E->next) { - LayeredTileSetSource *source = *tileset->get_source(E->value().cell.source_id); - if (!source || !source->has_tile(E->value().cell.get_atlas_coords()) || !source->has_alternative_tile(E->value().cell.get_atlas_coords(), E->value().cell.alternative_tile)) { - coords.insert(E->key()); - } - } - - for (RBSet::Element *E = coords.front(); E; E = E->next()) { - set_cell(E->get(), LayeredTileSet::INVALID_SOURCE, LayeredTileSetSource::INVALID_ATLAS_COORDS, LayeredTileSetSource::INVALID_TILE_ALTERNATIVE); - } -} - -bool LayeredTileMapLayer::has_body_rid(RID p_physics_body) const { - return bodies_coords.has(p_physics_body); -} - -Vector2i LayeredTileMapLayer::get_coords_for_body_rid(RID p_physics_body) const { - return bodies_coords[p_physics_body]; -} - -Ref LayeredTileMapLayer::get_effective_tile_set() const { - LayeredTileMapLayerGroup *tile_map_layer_group = Object::cast_to(get_parent()); - if (tile_map_layer_group) { - return tile_map_layer_group->get_tileset(); - } else { - return Ref(); - } -} - bool LayeredTileMapLayer::use_tile_data_runtime_update(const Vector2i &p_coords) { return call("_use_tile_data_runtime_update", p_coords); } diff --git a/modules/layered_tile_map/layered_tile_map_layer.h b/modules/layered_tile_map/layered_tile_map_layer.h index dc62d314f..675ad486a 100644 --- a/modules/layered_tile_map/layered_tile_map_layer.h +++ b/modules/layered_tile_map/layered_tile_map_layer.h @@ -32,7 +32,6 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "layered_tile_map.h" #include "layered_tile_set.h" #include "scene/2d/y_sort.h" @@ -43,6 +42,12 @@ #endif class LayeredTileSetAtlasSource; +class LayeredTileMap; + +enum LayeredTileMapLayerDataFormat { + LAYERED_TILE_MAP_LAYER_DATA_FORMAT_0 = 0, + LAYERED_TILE_MAP_LAYER_DATA_FORMAT_MAX, +}; class TerrainConstraint { private: @@ -235,14 +240,21 @@ class LayeredTileMapLayer : public YSort { GDCLASS(LayeredTileMapLayer, YSort); public: - enum VisibilityMode { - VISIBILITY_MODE_DEFAULT, - VISIBILITY_MODE_FORCE_SHOW, - VISIBILITY_MODE_FORCE_HIDE, + enum HighlightMode { + HIGHLIGHT_MODE_DEFAULT, + HIGHLIGHT_MODE_ABOVE, + HIGHLIGHT_MODE_BELOW, + }; + + enum DebugVisibilityMode { + DEBUG_VISIBILITY_MODE_DEFAULT, + DEBUG_VISIBILITY_MODE_FORCE_SHOW, + DEBUG_VISIBILITY_MODE_FORCE_HIDE, }; enum DirtyFlags { DIRTY_FLAGS_LAYER_ENABLED = 0, + DIRTY_FLAGS_LAYER_IN_TREE, DIRTY_FLAGS_LAYER_IN_CANVAS, DIRTY_FLAGS_LAYER_LOCAL_TRANSFORM, @@ -255,6 +267,7 @@ public: DIRTY_FLAGS_LAYER_TEXTURE_FILTER, DIRTY_FLAGS_LAYER_TEXTURE_REPEAT, DIRTY_FLAGS_LAYER_RENDERING_QUADRANT_SIZE, + DIRTY_FLAGS_LAYER_COLLISION_ENABLED, DIRTY_FLAGS_LAYER_USE_KINEMATIC_BODIES, DIRTY_FLAGS_LAYER_COLLISION_VISIBILITY_MODE, DIRTY_FLAGS_LAYER_NAVIGATION_ENABLED, @@ -275,23 +288,32 @@ public: DIRTY_FLAGS_LAYER_GROUP_SELECTED_LAYERS, DIRTY_FLAGS_LAYER_GROUP_HIGHLIGHT_SELECTED, - DIRTY_FLAGS_LAYER_GROUP_TILE_SET, + + DIRTY_FLAGS_TILE_SET, DIRTY_FLAGS_MAX, }; private: - // Exposed properties. + // Properties. + HashMap tile_map_layer_data; + bool enabled = true; + + Ref tile_set; + + HighlightMode highlight_mode = HIGHLIGHT_MODE_DEFAULT; + int y_sort_origin = 0; int rendering_quadrant_size = 16; - + + bool collision_enabled = true; bool use_kinematic_bodies = false; - VisibilityMode collision_visibility_mode = VISIBILITY_MODE_DEFAULT; + DebugVisibilityMode collision_visibility_mode = DEBUG_VISIBILITY_MODE_DEFAULT; bool navigation_enabled = true; RID navigation_map_override; - VisibilityMode navigation_visibility_mode = VISIBILITY_MODE_DEFAULT; + DebugVisibilityMode navigation_visibility_mode = DEBUG_VISIBILITY_MODE_DEFAULT; // Internal. HashMap tile_map; @@ -385,6 +407,8 @@ private: RBSet _get_terrain_constraints_from_added_pattern(const Vector2i &p_position, int p_terrain_set, LayeredTileSet::TerrainsPattern p_terrains_pattern) const; RBSet _get_terrain_constraints_from_painted_cells_list(const RBSet &p_painted, int p_terrain_set, bool p_ignore_empty_terrains) const; + void _tile_set_changed(); + void _renamed(); void _update_notify_local_transform(); @@ -400,11 +424,19 @@ private: protected: void _notification(int p_what); + static void _bind_methods(); public: // LayeredTileMap node. void set_as_tile_map_internal_node(int p_index); + + int get_index_in_tile_map() const { + return layer_index_in_tile_map_node; + } + const HashMap &get_tile_map_layer_data() const { + return tile_map_layer_data; + } // Rect caching. Rect2 get_rect(bool &r_changed) const; @@ -416,26 +448,26 @@ public: HashMap terrain_fill_pattern(const Vector &p_coords_array, int p_terrain_set, LayeredTileSet::TerrainsPattern p_terrains_pattern, bool p_ignore_empty_terrains = true) const; // Not exposed. // Not exposed to users. - LayeredTileMapCell get_cell(const Vector2i &p_coords, bool p_use_proxies = false) const; + LayeredTileMapCell get_cell(const Vector2i &p_coords) const; - // For LayeredTileMap node's use. - void set_tile_data(LayeredTileMapDataFormat p_format, const Vector &p_data); - Vector get_tile_data() const; - void notify_tile_map_layer_group_change(DirtyFlags p_what); + ////////////// Exposed functions ////////////// - void update_internals(); - void notify_runtime_tile_data_update(); - - // --- Exposed in LayeredTileMap --- - // Cells manipulation. - void set_cell(const Vector2i &p_coords, int p_source_id = LayeredTileSet::INVALID_SOURCE, const Vector2i p_atlas_coords = LayeredTileSetSource::INVALID_ATLAS_COORDS, int p_alternative_tile = 0); + // --- Cells manipulation --- + // Generic cells manipulations and data access. + void set_cell(const Vector2i &p_coords, int p_source_id = LayeredTileSet::INVALID_SOURCE, const Vector2i &p_atlas_coords = LayeredTileSetSource::INVALID_ATLAS_COORDS, int p_alternative_tile = 0); void erase_cell(const Vector2i &p_coords); - int get_cell_source_id(const Vector2i &p_coords, bool p_use_proxies = false) const; - Vector2i get_cell_atlas_coords(const Vector2i &p_coords, bool p_use_proxies = false) const; - int get_cell_alternative_tile(const Vector2i &p_coords, bool p_use_proxies = false) const; - LayeredTileData *get_cell_tile_data(const Vector2i &p_coords, bool p_use_proxies = false) const; // Helper method to make accessing the data easier. + void fix_invalid_tiles(); void clear(); + + 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; + LayeredTileData *get_cell_tile_data(const Vector2i &p_coords) const; // Helper method to make accessing the data easier. + + PoolVector2iArray get_used_cells() const; + PoolVector2iArray get_used_cells_by_id(int p_source_id = LayeredTileSet::INVALID_SOURCE, const Vector2i &p_atlas_coords = LayeredTileSetSource::INVALID_ATLAS_COORDS, int p_alternative_tile = LayeredTileSetSource::INVALID_TILE_ALTERNATIVE) const; + Rect2i get_used_rect() const; // Patterns. Ref get_pattern(PoolVector2iArray p_coords_array); @@ -445,14 +477,33 @@ public: void set_cells_terrain_connect(PoolVector2iArray p_cells, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains = true); void set_cells_terrain_path(PoolVector2iArray p_path, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains = true); - // Cells usage. - PoolVector2iArray get_used_cells() const; - PoolVector2iArray get_used_cells_by_id(int p_source_id = LayeredTileSet::INVALID_SOURCE, const Vector2i p_atlas_coords = LayeredTileSetSource::INVALID_ATLAS_COORDS, int p_alternative_tile = LayeredTileSetSource::INVALID_TILE_ALTERNATIVE) const; - Rect2i get_used_rect() const; + // --- Physics helpers --- + bool has_body_rid(RID p_physics_body) const; + Vector2i get_coords_for_body_rid(RID p_physics_body) const; // For finding tiles from collision. + + // --- Runtime --- + void update_internals(); + void notify_runtime_tile_data_update(); + + // --- Shortcuts to methods defined in TileSet --- + Vector2i map_pattern(const Vector2i &p_position_in_tilemap, const Vector2i &p_coords_in_pattern, Ref p_pattern); + PoolVector2iArray get_surrounding_cells(const Vector2i &p_coords); + Vector2i get_neighbor_cell(const Vector2i &p_coords, LayeredTileSet::CellNeighbor p_cell_neighbor) const; + Vector2 map_to_local(const Vector2i &p_pos) const; + Vector2i local_to_map(const Vector2 &p_pos) const; + + // --- Accessors --- + void set_tile_map_data_from_array(const Vector &p_data); + Vector get_tile_map_data_as_array() const; - // Layer properties. void set_enabled(bool p_enabled); bool is_enabled() const; + void set_tile_set(const Ref &p_tile_set); + Ref get_tile_set() const; + + void set_highlight_mode(HighlightMode p_highlight_mode); + HighlightMode get_highlight_mode() const; + virtual void set_self_modulate(const Color &p_self_modulate); virtual void set_y_sort_enabled(bool p_y_sort_enabled); void set_y_sort_origin(int p_y_sort_origin); @@ -462,17 +513,24 @@ public: void set_rendering_quadrant_size(int p_size); int get_rendering_quadrant_size() const; + void set_collision_enabled(bool p_enabled); + bool is_collision_enabled() const; void set_use_kinematic_bodies(bool p_use_kinematic_bodies); bool is_using_kinematic_bodies() const; - void set_collision_visibility_mode(VisibilityMode p_show_collision); - VisibilityMode get_collision_visibility_mode() const; + void set_collision_visibility_mode(DebugVisibilityMode p_show_collision); + DebugVisibilityMode get_collision_visibility_mode() const; void set_navigation_enabled(bool p_enabled); bool is_navigation_enabled() const; void set_navigation_map(RID p_map); RID get_navigation_map() const; - void set_navigation_visibility_mode(VisibilityMode p_show_navigation); - VisibilityMode get_navigation_visibility_mode() const; + void set_navigation_visibility_mode(DebugVisibilityMode p_show_navigation); + DebugVisibilityMode get_navigation_visibility_mode() const; + + // Virtual function to modify the LayeredTileData at runtime. + bool use_tile_data_runtime_update(const Vector2i &p_coords); + void tile_data_runtime_update(const Vector2i &p_coords, LayeredTileData *p_tile_data); + //VertexLights2D #ifdef MODULE_VERTEX_LIGHTS_2D_ENABLED @@ -488,25 +546,11 @@ public: void set_rao_strength(const real_t p_strength); real_t get_rao_strength() const; #endif - - // Fixing and clearing methods. - void fix_invalid_tiles(); - - // Find coords for body. - bool has_body_rid(RID p_physics_body) const; - Vector2i get_coords_for_body_rid(RID p_physics_body) const; // For finding tiles from collision. - - // Helper. - Ref get_effective_tile_set() const; - - // Virtual function to modify the LayeredTileData at runtime. - bool use_tile_data_runtime_update(const Vector2i &p_coords); - void tile_data_runtime_update(const Vector2i &p_coords, LayeredTileData *p_tile_data); - - // --- - + LayeredTileMapLayer(); ~LayeredTileMapLayer(); }; +VARIANT_ENUM_CAST(LayeredTileMapLayer::DebugVisibilityMode); + #endif // TILE_MAP_LAYER_H diff --git a/modules/layered_tile_map/layered_tile_map_layer_group.cpp b/modules/layered_tile_map/layered_tile_map_layer_group.cpp deleted file mode 100644 index 0112464d0..000000000 --- a/modules/layered_tile_map/layered_tile_map_layer_group.cpp +++ /dev/null @@ -1,151 +0,0 @@ -/*************************************************************************/ -/* layered_tile_map_layer_group.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* PANDEMONIUM ENGINE */ -/* https://github.com/Relintai/pandemonium_engine */ -/*************************************************************************/ -/* Copyright (c) 2022-present Péter Magyar. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* */ -/* 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 "layered_tile_map_layer_group.h" - -#include "core/core_string_names.h" -#include "layered_tile_map_layer.h" -#include "layered_tile_set.h" - -#ifdef TOOLS_ENABLED - -void LayeredTileMapLayerGroup::_cleanup_selected_layers() { - for (int i = 0; i < selected_layers.size(); i++) { - const String name = selected_layers[i]; - LayeredTileMapLayer *layer = Object::cast_to(get_node_or_null(name)); - if (!layer) { - selected_layers.remove(i); - i--; - } - } -} - -#endif // TOOLS_ENABLED - -void LayeredTileMapLayerGroup::_tile_set_changed() { - for (int i = 0; i < get_child_count(); i++) { - LayeredTileMapLayer *layer = Object::cast_to(get_child(i)); - if (layer) { - layer->notify_tile_map_layer_group_change(LayeredTileMapLayer::DIRTY_FLAGS_LAYER_GROUP_TILE_SET); - } - } - - update_configuration_warning(); -} - -#ifdef TOOLS_ENABLED - -void LayeredTileMapLayerGroup::set_selected_layers(Vector p_layer_names) { - selected_layers = p_layer_names; - _cleanup_selected_layers(); - - // Update the layers modulation. - for (int i = 0; i < get_child_count(); i++) { - LayeredTileMapLayer *layer = Object::cast_to(get_child(i)); - if (layer) { - layer->notify_tile_map_layer_group_change(LayeredTileMapLayer::DIRTY_FLAGS_LAYER_GROUP_SELECTED_LAYERS); - } - } -} - -Vector LayeredTileMapLayerGroup::get_selected_layers() const { - return selected_layers; -} - -void LayeredTileMapLayerGroup::set_highlight_selected_layer(bool p_highlight_selected_layer) { - if (highlight_selected_layer == p_highlight_selected_layer) { - return; - } - - highlight_selected_layer = p_highlight_selected_layer; - - for (int i = 0; i < get_child_count(); i++) { - LayeredTileMapLayer *layer = Object::cast_to(get_child(i)); - if (layer) { - layer->notify_tile_map_layer_group_change(LayeredTileMapLayer::DIRTY_FLAGS_LAYER_GROUP_HIGHLIGHT_SELECTED); - } - } -} - -bool LayeredTileMapLayerGroup::is_highlighting_selected_layer() const { - return highlight_selected_layer; -} - -#endif // TOOLS_ENABLED - -void LayeredTileMapLayerGroup::remove_child_notify(Node *p_child) { -#ifdef TOOLS_ENABLED - _cleanup_selected_layers(); -#endif // TOOLS_ENABLED -} - -void LayeredTileMapLayerGroup::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_tileset", "tileset"), &LayeredTileMapLayerGroup::set_tileset); - ClassDB::bind_method(D_METHOD("get_tileset"), &LayeredTileMapLayerGroup::get_tileset); - - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "tile_set", PROPERTY_HINT_RESOURCE_TYPE, "LayeredTileSet"), "set_tileset", "get_tileset"); - - ClassDB::bind_method(D_METHOD("_tile_set_changed"), &LayeredTileMapLayerGroup::_tile_set_changed); -} - -void LayeredTileMapLayerGroup::set_tileset(const Ref &p_tileset) { - if (p_tileset == tile_set) { - return; - } - - // Set the tileset, registering to its changes. - if (tile_set.is_valid()) { - tile_set->disconnect(CoreStringNames::get_singleton()->changed, this, "_tile_set_changed"); - } - - tile_set = p_tileset; - - if (tile_set.is_valid()) { - tile_set->connect(CoreStringNames::get_singleton()->changed, this, "_tile_set_changed"); - } - - for (int i = 0; i < get_child_count(); i++) { - LayeredTileMapLayer *layer = Object::cast_to(get_child(i)); - if (layer) { - layer->notify_tile_map_layer_group_change(LayeredTileMapLayer::DIRTY_FLAGS_LAYER_GROUP_TILE_SET); - } - } -} - -Ref LayeredTileMapLayerGroup::get_tileset() const { - return tile_set; -} - -LayeredTileMapLayerGroup::~LayeredTileMapLayerGroup() { - if (tile_set.is_valid()) { - tile_set->disconnect(CoreStringNames::get_singleton()->changed, this, "_tile_set_changed"); - } -} diff --git a/modules/layered_tile_map/layered_tile_map_layer_group.h b/modules/layered_tile_map/layered_tile_map_layer_group.h deleted file mode 100644 index f236e95fc..000000000 --- a/modules/layered_tile_map/layered_tile_map_layer_group.h +++ /dev/null @@ -1,74 +0,0 @@ -#ifndef LAYERED_TILE_MAP_LAYER_GROUP_H -#define LAYERED_TILE_MAP_LAYER_GROUP_H - -/*************************************************************************/ -/* layered_tile_map_layer_group.h */ -/*************************************************************************/ -/* This file is part of: */ -/* PANDEMONIUM ENGINE */ -/* https://github.com/Relintai/pandemonium_engine */ -/*************************************************************************/ -/* Copyright (c) 2022-present Péter Magyar. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* */ -/* 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 "scene/2d/y_sort.h" - -class LayeredTileSet; - -class LayeredTileMapLayerGroup : public YSort { - GDCLASS(LayeredTileMapLayerGroup, YSort); - -private: - mutable Vector selected_layers; - bool highlight_selected_layer = true; - -#ifdef TOOLS_ENABLED - void _cleanup_selected_layers(); -#endif - void _tile_set_changed(); - -protected: - Ref tile_set; - - virtual void remove_child_notify(Node *p_child); - - static void _bind_methods(); - -public: -#ifdef TOOLS_ENABLED - // For editor use. - void set_selected_layers(Vector p_layer_names); - Vector get_selected_layers() const; - void set_highlight_selected_layer(bool p_highlight_selected_layer); - bool is_highlighting_selected_layer() const; -#endif - - // Accessors. - void set_tileset(const Ref &p_tileset); - Ref get_tileset() const; - - ~LayeredTileMapLayerGroup(); -}; - -#endif // TILE_MAP_LAYER_GROUP_H diff --git a/modules/layered_tile_map/layered_tile_set.cpp b/modules/layered_tile_map/layered_tile_set.cpp index 036e31236..3a2377c2e 100644 --- a/modules/layered_tile_map/layered_tile_set.cpp +++ b/modules/layered_tile_map/layered_tile_set.cpp @@ -1433,10 +1433,10 @@ int LayeredTileSet::get_patterns_count() { return patterns.size(); } -RBSet LayeredTileSet::get_terrains_pattern_set(int p_terrain_set) { +RBSet LayeredTileSet::get_terrains_pattern_set(int p_terrain_set) const { ERR_FAIL_INDEX_V(p_terrain_set, terrain_sets.size(), RBSet()); - _update_terrains_cache(); + const_cast(this)->_update_terrains_cache(); RBSet output; @@ -1449,17 +1449,18 @@ RBSet LayeredTileSet::get_terrains_pattern_set( return output; } -RBSet LayeredTileSet::get_tiles_for_terrains_pattern(int p_terrain_set, TerrainsPattern p_terrain_tile_pattern) { +RBSet LayeredTileSet::get_tiles_for_terrains_pattern(int p_terrain_set, TerrainsPattern p_terrain_tile_pattern) const { ERR_FAIL_INDEX_V(p_terrain_set, terrain_sets.size(), RBSet()); - _update_terrains_cache(); + const_cast(this)->_update_terrains_cache(); return per_terrain_pattern_tiles[p_terrain_set][p_terrain_tile_pattern]; } -LayeredTileMapCell LayeredTileSet::get_random_tile_from_terrains_pattern(int p_terrain_set, LayeredTileSet::TerrainsPattern p_terrain_tile_pattern) { +LayeredTileMapCell LayeredTileSet::get_random_tile_from_terrains_pattern(int p_terrain_set, LayeredTileSet::TerrainsPattern p_terrain_tile_pattern) const { ERR_FAIL_INDEX_V(p_terrain_set, terrain_sets.size(), LayeredTileMapCell()); - _update_terrains_cache(); + + const_cast(this)->_update_terrains_cache(); // Count the sum of probabilities. double sum = 0.0; diff --git a/modules/layered_tile_map/layered_tile_set.h b/modules/layered_tile_map/layered_tile_set.h index a7036a38e..99e434414 100644 --- a/modules/layered_tile_map/layered_tile_set.h +++ b/modules/layered_tile_map/layered_tile_set.h @@ -524,9 +524,9 @@ public: int get_patterns_count(); // Terrains. - RBSet get_terrains_pattern_set(int p_terrain_set); - RBSet get_tiles_for_terrains_pattern(int p_terrain_set, TerrainsPattern p_terrain_tile_pattern); - LayeredTileMapCell get_random_tile_from_terrains_pattern(int p_terrain_set, TerrainsPattern p_terrain_tile_pattern); + RBSet get_terrains_pattern_set(int p_terrain_set) const; + RBSet get_tiles_for_terrains_pattern(int p_terrain_set, TerrainsPattern p_terrain_tile_pattern) const; + LayeredTileMapCell get_random_tile_from_terrains_pattern(int p_terrain_set, TerrainsPattern p_terrain_tile_pattern) const; // Helpers Vector get_tile_shape_polygon() const; diff --git a/modules/layered_tile_map/register_types.cpp b/modules/layered_tile_map/register_types.cpp index 6a0c1722b..253140041 100644 --- a/modules/layered_tile_map/register_types.cpp +++ b/modules/layered_tile_map/register_types.cpp @@ -34,7 +34,6 @@ #include "geometry_parser/layered_tilemap_navigation_geometry_parser_2d.h" #include "servers/navigation/navigation_mesh_generator.h" -#include "layered_tile_map_layer_group.h" #include "layered_tile_map_layer.h" #include "layered_tile_map.h" #include "layered_tile_set.h" @@ -47,7 +46,7 @@ void register_layered_tile_map_types(ModuleRegistrationLevel p_level) { if (p_level == MODULE_REGISTRATION_LEVEL_SCENE) { ClassDB::register_class(); ClassDB::register_class(); - ClassDB::register_class(); + ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class();