diff options
Diffstat (limited to 'scene/2d/tile_map.cpp')
-rw-r--r-- | scene/2d/tile_map.cpp | 4145 |
1 files changed, 2156 insertions, 1989 deletions
diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index 8f41bb6146..99a79e023d 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -29,7 +29,9 @@ /**************************************************************************/ #include "tile_map.h" +#include "tile_map.compat.inc" +#include "core/core_string_names.h" #include "core/io/marshalls.h" #include "scene/resources/world_2d.h" #include "servers/navigation_server_2d.h" @@ -38,793 +40,17 @@ #include "servers/navigation_server_3d.h" #endif // DEBUG_ENABLED -HashMap<Vector2i, TileSet::CellNeighbor> TileMap::TerrainConstraint::get_overlapping_coords_and_peering_bits() const { - HashMap<Vector2i, TileSet::CellNeighbor> output; - - ERR_FAIL_COND_V(is_center_bit(), output); - - Ref<TileSet> ts = tile_map->get_tileset(); - ERR_FAIL_COND_V(!ts.is_valid(), output); - - TileSet::TileShape shape = ts->get_tile_shape(); - if (shape == TileSet::TILE_SHAPE_SQUARE) { - switch (bit) { - case 1: - output[base_cell_coords] = TileSet::CELL_NEIGHBOR_RIGHT_SIDE; - output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_LEFT_SIDE; - break; - case 2: - output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER; - output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER; - output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER; - output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER; - break; - case 3: - output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_SIDE; - output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_SIDE; - break; - default: - ERR_FAIL_V(output); - } - } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC) { - switch (bit) { - case 1: - output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE; - output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE; - break; - case 2: - output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_CORNER; - output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_LEFT_CORNER; - output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_CORNER)] = TileSet::CELL_NEIGHBOR_TOP_CORNER; - output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_RIGHT_CORNER; - break; - case 3: - output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE; - output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE; - break; - default: - ERR_FAIL_V(output); - } - } else { - // Half offset shapes. - TileSet::TileOffsetAxis offset_axis = ts->get_tile_offset_axis(); - if (offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { - switch (bit) { - case 1: - output[base_cell_coords] = TileSet::CELL_NEIGHBOR_RIGHT_SIDE; - output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_LEFT_SIDE; - break; - case 2: - output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER; - output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER; - output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_CORNER; - break; - case 3: - output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE; - output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE; - break; - case 4: - output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_CORNER; - output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER; - output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER; - break; - case 5: - output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE; - output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE; - break; - default: - ERR_FAIL_V(output); - } - } else { - switch (bit) { - case 1: - output[base_cell_coords] = TileSet::CELL_NEIGHBOR_RIGHT_CORNER; - output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER; - output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER; - break; - case 2: - output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE; - output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE; - break; - case 3: - output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER; - output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_LEFT_CORNER; - output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER; - break; - case 4: - output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_SIDE; - output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_SIDE; - break; - case 5: - output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE; - output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE; - break; - default: - ERR_FAIL_V(output); - } - } - } - return output; -} - -TileMap::TerrainConstraint::TerrainConstraint(const TileMap *p_tile_map, const Vector2i &p_position, int p_terrain) { - tile_map = p_tile_map; - - Ref<TileSet> ts = tile_map->get_tileset(); - ERR_FAIL_COND(!ts.is_valid()); - - bit = 0; - base_cell_coords = p_position; - terrain = p_terrain; -} - -TileMap::TerrainConstraint::TerrainConstraint(const TileMap *p_tile_map, const Vector2i &p_position, const TileSet::CellNeighbor &p_bit, int p_terrain) { - // The way we build the constraint make it easy to detect conflicting constraints. - tile_map = p_tile_map; - - Ref<TileSet> ts = tile_map->get_tileset(); - ERR_FAIL_COND(!ts.is_valid()); - - TileSet::TileShape shape = ts->get_tile_shape(); - if (shape == TileSet::TILE_SHAPE_SQUARE) { - switch (p_bit) { - case TileSet::CELL_NEIGHBOR_RIGHT_SIDE: - bit = 1; - base_cell_coords = p_position; - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: - bit = 2; - base_cell_coords = p_position; - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE: - bit = 3; - base_cell_coords = p_position; - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER: - bit = 2; - base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_LEFT_SIDE); - break; - case TileSet::CELL_NEIGHBOR_LEFT_SIDE: - bit = 1; - base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_LEFT_SIDE); - break; - case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER: - bit = 2; - base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER); - break; - case TileSet::CELL_NEIGHBOR_TOP_SIDE: - bit = 3; - base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_SIDE); - break; - case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER: - bit = 2; - base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_SIDE); - break; - default: - ERR_FAIL(); - break; - } - } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC) { - switch (p_bit) { - case TileSet::CELL_NEIGHBOR_RIGHT_CORNER: - bit = 2; - base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE: - bit = 1; - base_cell_coords = p_position; - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER: - bit = 2; - base_cell_coords = p_position; - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE: - bit = 3; - base_cell_coords = p_position; - break; - case TileSet::CELL_NEIGHBOR_LEFT_CORNER: - bit = 2; - base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE); - break; - case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE: - bit = 1; - base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE); - break; - case TileSet::CELL_NEIGHBOR_TOP_CORNER: - bit = 2; - base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_CORNER); - break; - case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE: - bit = 3; - base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE); - break; - default: - ERR_FAIL(); - break; - } - } else { - // Half-offset shapes - TileSet::TileOffsetAxis offset_axis = ts->get_tile_offset_axis(); - if (offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { - switch (p_bit) { - case TileSet::CELL_NEIGHBOR_RIGHT_SIDE: - bit = 1; - base_cell_coords = p_position; - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: - bit = 2; - base_cell_coords = p_position; - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE: - bit = 3; - base_cell_coords = p_position; - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER: - bit = 4; - base_cell_coords = p_position; - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE: - bit = 5; - base_cell_coords = p_position; - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER: - bit = 2; - base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_LEFT_SIDE); - break; - case TileSet::CELL_NEIGHBOR_LEFT_SIDE: - bit = 1; - base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_LEFT_SIDE); - break; - case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER: - bit = 4; - base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE); - break; - case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE: - bit = 3; - base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE); - break; - case TileSet::CELL_NEIGHBOR_TOP_CORNER: - bit = 2; - base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE); - break; - case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE: - bit = 5; - base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE); - break; - case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER: - bit = 4; - base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE); - break; - default: - ERR_FAIL(); - break; - } - } else { - switch (p_bit) { - case TileSet::CELL_NEIGHBOR_RIGHT_CORNER: - bit = 1; - base_cell_coords = p_position; - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE: - bit = 2; - base_cell_coords = p_position; - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: - bit = 3; - base_cell_coords = p_position; - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE: - bit = 4; - base_cell_coords = p_position; - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER: - bit = 1; - base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE: - bit = 5; - base_cell_coords = p_position; - break; - case TileSet::CELL_NEIGHBOR_LEFT_CORNER: - bit = 3; - base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE); - break; - case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE: - bit = 2; - base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE); - break; - case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER: - bit = 1; - base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE); - break; - case TileSet::CELL_NEIGHBOR_TOP_SIDE: - bit = 4; - base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_SIDE); - break; - case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER: - bit = 3; - base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_SIDE); - break; - case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE: - bit = 5; - base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE); - break; - default: - ERR_FAIL(); - break; - } - } - } - terrain = p_terrain; -} - -Vector2i TileMap::transform_coords_layout(const Vector2i &p_coords, TileSet::TileOffsetAxis p_offset_axis, TileSet::TileLayout p_from_layout, TileSet::TileLayout p_to_layout) { - // Transform to stacked layout. - Vector2i output = p_coords; - if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL) { - SWAP(output.x, output.y); - } - switch (p_from_layout) { - case TileSet::TILE_LAYOUT_STACKED: - break; - case TileSet::TILE_LAYOUT_STACKED_OFFSET: - if (output.y % 2) { - output.x -= 1; - } - break; - case TileSet::TILE_LAYOUT_STAIRS_RIGHT: - case TileSet::TILE_LAYOUT_STAIRS_DOWN: - if ((p_from_layout == TileSet::TILE_LAYOUT_STAIRS_RIGHT) ^ (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL)) { - if (output.y < 0 && bool(output.y % 2)) { - output = Vector2i(output.x + output.y / 2 - 1, output.y); - } else { - output = Vector2i(output.x + output.y / 2, output.y); - } - } else { - if (output.x < 0 && bool(output.x % 2)) { - output = Vector2i(output.x / 2 - 1, output.x + output.y * 2); - } else { - output = Vector2i(output.x / 2, output.x + output.y * 2); - } - } - break; - case TileSet::TILE_LAYOUT_DIAMOND_RIGHT: - case TileSet::TILE_LAYOUT_DIAMOND_DOWN: - if ((p_from_layout == TileSet::TILE_LAYOUT_DIAMOND_RIGHT) ^ (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL)) { - if ((output.x + output.y) < 0 && (output.x - output.y) % 2) { - output = Vector2i((output.x + output.y) / 2 - 1, output.y - output.x); - } else { - output = Vector2i((output.x + output.y) / 2, -output.x + output.y); - } - } else { - if ((output.x - output.y) < 0 && (output.x + output.y) % 2) { - output = Vector2i((output.x - output.y) / 2 - 1, output.x + output.y); - } else { - output = Vector2i((output.x - output.y) / 2, output.x + output.y); - } - } - break; - } - - switch (p_to_layout) { - case TileSet::TILE_LAYOUT_STACKED: - break; - case TileSet::TILE_LAYOUT_STACKED_OFFSET: - if (output.y % 2) { - output.x += 1; - } - break; - case TileSet::TILE_LAYOUT_STAIRS_RIGHT: - case TileSet::TILE_LAYOUT_STAIRS_DOWN: - if ((p_to_layout == TileSet::TILE_LAYOUT_STAIRS_RIGHT) ^ (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL)) { - if (output.y < 0 && (output.y % 2)) { - output = Vector2i(output.x - output.y / 2 + 1, output.y); - } else { - output = Vector2i(output.x - output.y / 2, output.y); - } - } else { - if (output.y % 2) { - if (output.y < 0) { - output = Vector2i(2 * output.x + 1, -output.x + output.y / 2 - 1); - } else { - output = Vector2i(2 * output.x + 1, -output.x + output.y / 2); - } - } else { - output = Vector2i(2 * output.x, -output.x + output.y / 2); - } - } - break; - case TileSet::TILE_LAYOUT_DIAMOND_RIGHT: - case TileSet::TILE_LAYOUT_DIAMOND_DOWN: - if ((p_to_layout == TileSet::TILE_LAYOUT_DIAMOND_RIGHT) ^ (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL)) { - if (output.y % 2) { - if (output.y > 0) { - output = Vector2i(output.x - output.y / 2, output.x + output.y / 2 + 1); - } else { - output = Vector2i(output.x - output.y / 2 + 1, output.x + output.y / 2); - } - } else { - output = Vector2i(output.x - output.y / 2, output.x + output.y / 2); - } - } else { - if (output.y % 2) { - if (output.y < 0) { - output = Vector2i(output.x + output.y / 2, -output.x + output.y / 2 - 1); - } else { - output = Vector2i(output.x + output.y / 2 + 1, -output.x + output.y / 2); - } - } else { - output = Vector2i(output.x + output.y / 2, -output.x + output.y / 2); - } - } - break; - } - - if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL) { - SWAP(output.x, output.y); - } - - return output; -} - -int TileMap::get_effective_quadrant_size(int p_layer) const { - ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), 1); - - // When using YSort, the quadrant size is reduced to 1 to have one CanvasItem per quadrant - if (is_y_sort_enabled() && layers[p_layer].y_sort_enabled) { - return 1; - } else { - return quadrant_size; - } -} - -void TileMap::set_selected_layer(int p_layer_id) { - ERR_FAIL_COND(p_layer_id < -1 || p_layer_id >= (int)layers.size()); - selected_layer = p_layer_id; - emit_signal(SNAME("changed")); - - // Update the layers modulation. - for (unsigned int layer = 0; layer < layers.size(); layer++) { - _rendering_update_layer(layer); - } -} - -int TileMap::get_selected_layer() const { - return selected_layer; -} - -void TileMap::_notification(int p_what) { - switch (p_what) { - case NOTIFICATION_ENTER_TREE: { - _clear_internals(); - _recreate_internals(); - } break; - - case NOTIFICATION_EXIT_TREE: { - _clear_internals(); - } break; - } - - // Transfers the notification to tileset plugins. - if (tile_set.is_valid()) { - _rendering_notification(p_what); - _physics_notification(p_what); - _navigation_notification(p_what); - } -} - -Ref<TileSet> TileMap::get_tileset() const { - return tile_set; -} - -void TileMap::set_tileset(const Ref<TileSet> &p_tileset) { - if (p_tileset == tile_set) { - return; - } - - // Set the tileset, registering to its changes. - if (tile_set.is_valid()) { - tile_set->disconnect_changed(callable_mp(this, &TileMap::_tile_set_changed)); - } - - if (!p_tileset.is_valid()) { - _clear_internals(); - } - - tile_set = p_tileset; - - if (tile_set.is_valid()) { - tile_set->connect_changed(callable_mp(this, &TileMap::_tile_set_changed)); - _clear_internals(); - _recreate_internals(); - } - - emit_signal(SNAME("changed")); -} - -void TileMap::set_quadrant_size(int p_size) { - ERR_FAIL_COND_MSG(p_size < 1, "TileMapQuadrant size cannot be smaller than 1."); - - quadrant_size = p_size; - _clear_internals(); - _recreate_internals(); - emit_signal(SNAME("changed")); -} - -int TileMap::get_quadrant_size() const { - return quadrant_size; -} - -int TileMap::get_layers_count() const { - return layers.size(); -} - -void TileMap::add_layer(int p_to_pos) { - if (p_to_pos < 0) { - p_to_pos = layers.size() + p_to_pos + 1; - } - - ERR_FAIL_INDEX(p_to_pos, (int)layers.size() + 1); - - // Must clear before adding the layer. - _clear_internals(); - - layers.insert(p_to_pos, TileMapLayer()); - _recreate_internals(); - notify_property_list_changed(); - - emit_signal(SNAME("changed")); - - update_configuration_warnings(); -} - -void TileMap::move_layer(int p_layer, int p_to_pos) { - ERR_FAIL_INDEX(p_layer, (int)layers.size()); - ERR_FAIL_INDEX(p_to_pos, (int)layers.size() + 1); - - // Clear before shuffling layers. - _clear_internals(); - - TileMapLayer tl = layers[p_layer]; - layers.insert(p_to_pos, tl); - layers.remove_at(p_to_pos < p_layer ? p_layer + 1 : p_layer); - _recreate_internals(); - notify_property_list_changed(); - - if (selected_layer == p_layer) { - selected_layer = p_to_pos < p_layer ? p_to_pos - 1 : p_to_pos; - } - - emit_signal(SNAME("changed")); - - update_configuration_warnings(); -} - -void TileMap::remove_layer(int p_layer) { - ERR_FAIL_INDEX(p_layer, (int)layers.size()); - - // Clear before removing the layer. - _clear_internals(); - - layers.remove_at(p_layer); - _recreate_internals(); - notify_property_list_changed(); - - if (selected_layer >= p_layer) { - selected_layer -= 1; - } - - emit_signal(SNAME("changed")); - - update_configuration_warnings(); -} - -void TileMap::set_layer_name(int p_layer, String p_name) { - if (p_layer < 0) { - p_layer = layers.size() + p_layer; - } - ERR_FAIL_INDEX(p_layer, (int)layers.size()); - - if (layers[p_layer].name == p_name) { - return; - } - layers[p_layer].name = p_name; - emit_signal(SNAME("changed")); -} - -String TileMap::get_layer_name(int p_layer) const { - ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), String()); - return layers[p_layer].name; -} - -void TileMap::set_layer_enabled(int p_layer, bool p_enabled) { - if (p_layer < 0) { - p_layer = layers.size() + p_layer; - } - ERR_FAIL_INDEX(p_layer, (int)layers.size()); - - if (layers[p_layer].enabled == p_enabled) { - return; - } - layers[p_layer].enabled = p_enabled; - _clear_layer_internals(p_layer); - _recreate_layer_internals(p_layer); - emit_signal(SNAME("changed")); - - update_configuration_warnings(); -} - -bool TileMap::is_layer_enabled(int p_layer) const { - ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), false); - return layers[p_layer].enabled; -} - -void TileMap::set_layer_modulate(int p_layer, Color p_modulate) { - if (p_layer < 0) { - p_layer = layers.size() + p_layer; - } - ERR_FAIL_INDEX(p_layer, (int)layers.size()); - - if (layers[p_layer].modulate == p_modulate) { - return; - } - layers[p_layer].modulate = p_modulate; - _rendering_update_layer(p_layer); - emit_signal(SNAME("changed")); -} - -Color TileMap::get_layer_modulate(int p_layer) const { - ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), Color()); - return layers[p_layer].modulate; -} - -void TileMap::set_layer_y_sort_enabled(int p_layer, bool p_y_sort_enabled) { - if (p_layer < 0) { - p_layer = layers.size() + p_layer; - } - ERR_FAIL_INDEX(p_layer, (int)layers.size()); - - if (layers[p_layer].y_sort_enabled == p_y_sort_enabled) { - return; - } - layers[p_layer].y_sort_enabled = p_y_sort_enabled; - _clear_layer_internals(p_layer); - _recreate_layer_internals(p_layer); - emit_signal(SNAME("changed")); - - update_configuration_warnings(); -} - -bool TileMap::is_layer_y_sort_enabled(int p_layer) const { - ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), false); - return layers[p_layer].y_sort_enabled; -} - -void TileMap::set_layer_y_sort_origin(int p_layer, int p_y_sort_origin) { - if (p_layer < 0) { - p_layer = layers.size() + p_layer; - } - ERR_FAIL_INDEX(p_layer, (int)layers.size()); +Vector2i TileMapLayer::_coords_to_quadrant_coords(const Vector2i &p_coords) const { + int quad_size = get_effective_quadrant_size(); - if (layers[p_layer].y_sort_origin == p_y_sort_origin) { - return; - } - layers[p_layer].y_sort_origin = p_y_sort_origin; - _clear_layer_internals(p_layer); - _recreate_layer_internals(p_layer); - emit_signal(SNAME("changed")); -} - -int TileMap::get_layer_y_sort_origin(int p_layer) const { - ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), false); - return layers[p_layer].y_sort_origin; -} - -void TileMap::set_layer_z_index(int p_layer, int p_z_index) { - if (p_layer < 0) { - p_layer = layers.size() + p_layer; - } - ERR_FAIL_INDEX(p_layer, (int)layers.size()); - - if (layers[p_layer].z_index == p_z_index) { - return; - } - layers[p_layer].z_index = p_z_index; - _rendering_update_layer(p_layer); - emit_signal(SNAME("changed")); - - update_configuration_warnings(); -} - -int TileMap::get_layer_z_index(int p_layer) const { - ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), false); - return layers[p_layer].z_index; -} - -void TileMap::set_collision_animatable(bool p_enabled) { - if (collision_animatable == p_enabled) { - return; - } - collision_animatable = p_enabled; - _clear_internals(); - set_notify_local_transform(p_enabled); - set_physics_process_internal(p_enabled); - _recreate_internals(); - emit_signal(SNAME("changed")); -} - -bool TileMap::is_collision_animatable() const { - return collision_animatable; -} - -void TileMap::set_collision_visibility_mode(TileMap::VisibilityMode p_show_collision) { - if (collision_visibility_mode == p_show_collision) { - return; - } - collision_visibility_mode = p_show_collision; - _clear_internals(); - _recreate_internals(); - emit_signal(SNAME("changed")); -} - -TileMap::VisibilityMode TileMap::get_collision_visibility_mode() { - return collision_visibility_mode; -} - -void TileMap::set_navigation_visibility_mode(TileMap::VisibilityMode p_show_navigation) { - if (navigation_visibility_mode == p_show_navigation) { - return; - } - navigation_visibility_mode = p_show_navigation; - _clear_internals(); - _recreate_internals(); - emit_signal(SNAME("changed")); -} - -TileMap::VisibilityMode TileMap::get_navigation_visibility_mode() { - return navigation_visibility_mode; -} - -void TileMap::set_navigation_map(int p_layer, RID p_map) { - ERR_FAIL_INDEX(p_layer, (int)layers.size()); - ERR_FAIL_COND_MSG(!is_inside_tree(), "A TileMap navigation map can only be changed while inside the SceneTree."); - layers[p_layer].navigation_map = p_map; - layers[p_layer].uses_world_navigation_map = p_map == get_world_2d()->get_navigation_map(); -} - -RID TileMap::get_navigation_map(int p_layer) const { - ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), RID()); - if (layers[p_layer].navigation_map.is_valid()) { - return layers[p_layer].navigation_map; - } - return RID(); -} - -void TileMap::set_y_sort_enabled(bool p_enable) { - if (is_y_sort_enabled() == p_enable) { - return; - } - Node2D::set_y_sort_enabled(p_enable); - _clear_internals(); - _recreate_internals(); - emit_signal(SNAME("changed")); - update_configuration_warnings(); -} - -Vector2i TileMap::_coords_to_quadrant_coords(int p_layer, const Vector2i &p_coords) const { - int quad_size = get_effective_quadrant_size(p_layer); - - // Rounding down, instead of simply rounding towards zero (truncating) + // Rounding down, instead of simply rounding towards zero (truncating). return Vector2i( p_coords.x > 0 ? p_coords.x / quad_size : (p_coords.x - (quad_size - 1)) / quad_size, p_coords.y > 0 ? p_coords.y / quad_size : (p_coords.y - (quad_size - 1)) / quad_size); } -HashMap<Vector2i, TileMapQuadrant>::Iterator TileMap::_create_quadrant(int p_layer, const Vector2i &p_qk) { - ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), nullptr); - +HashMap<Vector2i, TileMapQuadrant>::Iterator TileMapLayer::_create_quadrant(const Vector2i &p_qk) { TileMapQuadrant q; - q.layer = p_layer; q.coords = p_qk; rect_cache_dirty = true; @@ -833,156 +59,32 @@ HashMap<Vector2i, TileMapQuadrant>::Iterator TileMap::_create_quadrant(int p_lay RenderingServer *rs = RenderingServer::get_singleton(); q.debug_canvas_item = rs->canvas_item_create(); rs->canvas_item_set_z_index(q.debug_canvas_item, RS::CANVAS_ITEM_Z_MAX - 1); - rs->canvas_item_set_parent(q.debug_canvas_item, get_canvas_item()); + rs->canvas_item_set_parent(q.debug_canvas_item, tile_map_node->get_canvas_item()); - // Call the create_quadrant method on plugins + // Call the create_quadrant method on plugins. + const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); if (tile_set.is_valid()) { _rendering_create_quadrant(&q); } - return layers[p_layer].quadrant_map.insert(p_qk, q); + return quadrant_map.insert(p_qk, q); } -void TileMap::_make_quadrant_dirty(HashMap<Vector2i, TileMapQuadrant>::Iterator Q) { +void TileMapLayer::_make_quadrant_dirty(HashMap<Vector2i, TileMapQuadrant>::Iterator Q) { // Make the given quadrant dirty, then trigger an update later. TileMapQuadrant &q = Q->value; if (!q.dirty_list_element.in_list()) { - layers[q.layer].dirty_quadrant_list.add(&q.dirty_list_element); - } - _queue_update_dirty_quadrants(); -} - -void TileMap::_make_all_quadrants_dirty() { - // Make all quandrants dirty, then trigger an update later. - for (TileMapLayer &layer : layers) { - for (KeyValue<Vector2i, TileMapQuadrant> &E : layer.quadrant_map) { - if (!E.value.dirty_list_element.in_list()) { - layer.dirty_quadrant_list.add(&E.value.dirty_list_element); - } - } - } - _queue_update_dirty_quadrants(); -} - -void TileMap::_queue_update_dirty_quadrants() { - if (pending_update || !is_inside_tree()) { - return; - } - pending_update = true; - call_deferred(SNAME("_update_dirty_quadrants")); -} - -void TileMap::_update_dirty_quadrants() { - if (!pending_update) { - return; - } - if (!is_inside_tree() || !tile_set.is_valid()) { - pending_update = false; - return; - } - - for (unsigned int layer = 0; layer < layers.size(); layer++) { - SelfList<TileMapQuadrant>::List &dirty_quadrant_list = layers[layer].dirty_quadrant_list; - - // Update the coords cache. - for (SelfList<TileMapQuadrant> *q = dirty_quadrant_list.first(); q; q = q->next()) { - q->self()->map_to_local.clear(); - q->self()->local_to_map.clear(); - for (const Vector2i &E : q->self()->cells) { - Vector2i pk = E; - Vector2 pk_local_coords = map_to_local(pk); - q->self()->map_to_local[pk] = pk_local_coords; - q->self()->local_to_map[pk_local_coords] = pk; - } - } - - // Find TileData that need a runtime modification. - _build_runtime_update_tile_data(dirty_quadrant_list); - - // Call the update_dirty_quadrant method on plugins. - _rendering_update_dirty_quadrants(dirty_quadrant_list); - _physics_update_dirty_quadrants(dirty_quadrant_list); - _navigation_update_dirty_quadrants(dirty_quadrant_list); - _scenes_update_dirty_quadrants(dirty_quadrant_list); - - // Redraw the debug canvas_items. - RenderingServer *rs = RenderingServer::get_singleton(); - for (SelfList<TileMapQuadrant> *q = dirty_quadrant_list.first(); q; q = q->next()) { - rs->canvas_item_clear(q->self()->debug_canvas_item); - Transform2D xform; - xform.set_origin(map_to_local(q->self()->coords * get_effective_quadrant_size(layer))); - rs->canvas_item_set_transform(q->self()->debug_canvas_item, xform); - - _rendering_draw_quadrant_debug(q->self()); - _physics_draw_quadrant_debug(q->self()); - _navigation_draw_quadrant_debug(q->self()); - _scenes_draw_quadrant_debug(q->self()); - } - - // Clear the list - while (dirty_quadrant_list.first()) { - // Clear the runtime tile data. - for (const KeyValue<Vector2i, TileData *> &kv : dirty_quadrant_list.first()->self()->runtime_tile_data_cache) { - memdelete(kv.value); - } - - dirty_quadrant_list.remove(dirty_quadrant_list.first()); - } + dirty_quadrant_list.add(&q.dirty_list_element); } - - pending_update = false; - - _recompute_rect_cache(); + tile_map_node->queue_update_dirty_quadrants(); } -void TileMap::_recreate_layer_internals(int p_layer) { - ERR_FAIL_INDEX(p_layer, (int)layers.size()); - - // Make sure that _clear_internals() was called prior. - ERR_FAIL_COND_MSG(layers[p_layer].quadrant_map.size() > 0, "TileMap layer " + itos(p_layer) + " had a non-empty quadrant map."); - - if (!layers[p_layer].enabled) { - return; - } - - // Update the layer internals. - _rendering_update_layer(p_layer); - - // Update the layer internal navigation maps. - _navigation_update_layer(p_layer); - - // Recreate the quadrants. - const HashMap<Vector2i, TileMapCell> &tile_map = layers[p_layer].tile_map; - for (const KeyValue<Vector2i, TileMapCell> &E : tile_map) { - Vector2i qk = _coords_to_quadrant_coords(p_layer, Vector2i(E.key.x, E.key.y)); - - HashMap<Vector2i, TileMapQuadrant>::Iterator Q = layers[p_layer].quadrant_map.find(qk); - if (!Q) { - Q = _create_quadrant(p_layer, qk); - layers[p_layer].dirty_quadrant_list.add(&Q->value.dirty_list_element); - } - - Vector2i pk = E.key; - Q->value.cells.insert(pk); - - _make_quadrant_dirty(Q); - } - - _queue_update_dirty_quadrants(); -} - -void TileMap::_recreate_internals() { - for (unsigned int layer = 0; layer < layers.size(); layer++) { - _recreate_layer_internals(layer); - } -} - -void TileMap::_erase_quadrant(HashMap<Vector2i, TileMapQuadrant>::Iterator Q) { +void TileMapLayer::_erase_quadrant(HashMap<Vector2i, TileMapQuadrant>::Iterator Q) { // Remove a quadrant. TileMapQuadrant *q = &(Q->value); // Call the cleanup_quadrant method on plugins. - if (tile_set.is_valid()) { + if (tile_map_node->get_tileset().is_valid()) { _rendering_cleanup_quadrant(q); _physics_cleanup_quadrant(q); _navigation_cleanup_quadrant(q); @@ -991,217 +93,42 @@ void TileMap::_erase_quadrant(HashMap<Vector2i, TileMapQuadrant>::Iterator Q) { // Remove the quadrant from the dirty_list if it is there. if (q->dirty_list_element.in_list()) { - layers[q->layer].dirty_quadrant_list.remove(&(q->dirty_list_element)); + dirty_quadrant_list.remove(&(q->dirty_list_element)); } // Free the debug canvas item. RenderingServer *rs = RenderingServer::get_singleton(); rs->free(q->debug_canvas_item); - layers[q->layer].quadrant_map.remove(Q); + quadrant_map.remove(Q); rect_cache_dirty = true; } -void TileMap::_clear_layer_internals(int p_layer) { - ERR_FAIL_INDEX(p_layer, (int)layers.size()); - - // Clear quadrants. - while (layers[p_layer].quadrant_map.size()) { - _erase_quadrant(layers[p_layer].quadrant_map.begin()); - } - - // Clear the layers internals. - _rendering_cleanup_layer(p_layer); - - // Clear the layers internal navigation maps. - _navigation_cleanup_layer(p_layer); - - // Clear the dirty quadrants list. - while (layers[p_layer].dirty_quadrant_list.first()) { - layers[p_layer].dirty_quadrant_list.remove(layers[p_layer].dirty_quadrant_list.first()); - } -} - -void TileMap::_clear_internals() { - // Clear quadrants. - for (unsigned int layer = 0; layer < layers.size(); layer++) { - _clear_layer_internals(layer); - } -} - -void TileMap::_recompute_rect_cache() { - // Compute the displayed area of the tilemap. -#ifdef DEBUG_ENABLED - - if (!rect_cache_dirty) { - return; - } - - Rect2 r_total; - bool first = true; - for (unsigned int layer = 0; layer < layers.size(); layer++) { - for (const KeyValue<Vector2i, TileMapQuadrant> &E : layers[layer].quadrant_map) { - Rect2 r; - r.position = map_to_local(E.key * get_effective_quadrant_size(layer)); - r.expand_to(map_to_local((E.key + Vector2i(1, 0)) * get_effective_quadrant_size(layer))); - r.expand_to(map_to_local((E.key + Vector2i(1, 1)) * get_effective_quadrant_size(layer))); - r.expand_to(map_to_local((E.key + Vector2i(0, 1)) * get_effective_quadrant_size(layer))); - if (first) { - r_total = r; - first = false; - } else { - r_total = r_total.merge(r); - } - } - } - - bool changed = rect_cache != r_total; - - rect_cache = r_total; - - item_rect_changed(changed); - - rect_cache_dirty = false; -#endif -} - /////////////////////////////// Rendering ////////////////////////////////////// -void TileMap::_rendering_notification(int p_what) { - switch (p_what) { - case NOTIFICATION_ENTER_CANVAS: { - bool node_visible = is_visible_in_tree(); - for (TileMapLayer &layer : layers) { - for (KeyValue<Vector2i, TileMapQuadrant> &E_quadrant : layer.quadrant_map) { - TileMapQuadrant &q = E_quadrant.value; - for (const KeyValue<Vector2i, RID> &kv : q.occluders) { - Transform2D xform; - xform.set_origin(map_to_local(kv.key)); - RS::get_singleton()->canvas_light_occluder_attach_to_canvas(kv.value, get_canvas()); - RS::get_singleton()->canvas_light_occluder_set_transform(kv.value, get_global_transform() * xform); - RS::get_singleton()->canvas_light_occluder_set_enabled(kv.value, node_visible); - } - } - } - } break; - - case NOTIFICATION_VISIBILITY_CHANGED: { - bool node_visible = is_visible_in_tree(); - for (TileMapLayer &layer : layers) { - for (KeyValue<Vector2i, TileMapQuadrant> &E_quadrant : layer.quadrant_map) { - TileMapQuadrant &q = E_quadrant.value; - - // Update occluders transform. - for (const KeyValue<Vector2, Vector2i> &E_cell : q.local_to_map) { - Transform2D xform; - xform.set_origin(E_cell.key); - for (const KeyValue<Vector2i, RID> &kv : q.occluders) { - RS::get_singleton()->canvas_light_occluder_set_enabled(kv.value, node_visible); - } - } - } - } - } break; - - case NOTIFICATION_TRANSFORM_CHANGED: { - if (!is_inside_tree()) { - return; - } - for (TileMapLayer &layer : layers) { - for (KeyValue<Vector2i, TileMapQuadrant> &E_quadrant : layer.quadrant_map) { - TileMapQuadrant &q = E_quadrant.value; - - // Update occluders transform. - for (const KeyValue<Vector2i, RID> &kv : q.occluders) { - Transform2D xform; - xform.set_origin(map_to_local(kv.key)); - RenderingServer::get_singleton()->canvas_light_occluder_set_transform(kv.value, get_global_transform() * xform); - } - } - } - } break; - - case NOTIFICATION_DRAW: { - if (tile_set.is_valid()) { - RenderingServer::get_singleton()->canvas_item_set_sort_children_by_y(get_canvas_item(), is_y_sort_enabled()); - } - } break; - - case NOTIFICATION_EXIT_CANVAS: { - for (TileMapLayer &layer : layers) { - for (KeyValue<Vector2i, TileMapQuadrant> &E_quadrant : layer.quadrant_map) { - TileMapQuadrant &q = E_quadrant.value; - for (const KeyValue<Vector2i, RID> &kv : q.occluders) { - RS::get_singleton()->canvas_light_occluder_attach_to_canvas(kv.value, RID()); - } - } - } - } break; - } -} - -void TileMap::_navigation_update_layer(int p_layer) { - ERR_FAIL_INDEX(p_layer, (int)layers.size()); - ERR_FAIL_NULL(NavigationServer2D::get_singleton()); - - if (!layers[p_layer].navigation_map.is_valid()) { - if (p_layer == 0 && is_inside_tree()) { - // Use the default World2D navigation map for the first layer when empty. - layers[p_layer].navigation_map = get_world_2d()->get_navigation_map(); - layers[p_layer].uses_world_navigation_map = true; - } else { - RID new_layer_map = NavigationServer2D::get_singleton()->map_create(); - NavigationServer2D::get_singleton()->map_set_active(new_layer_map, true); - layers[p_layer].navigation_map = new_layer_map; - layers[p_layer].uses_world_navigation_map = false; - } - } -} - -void TileMap::_navigation_cleanup_layer(int p_layer) { - ERR_FAIL_INDEX(p_layer, (int)layers.size()); - ERR_FAIL_NULL(NavigationServer2D::get_singleton()); - - if (layers[p_layer].navigation_map.is_valid()) { - if (layers[p_layer].uses_world_navigation_map) { - // Do not delete the World2D default navigation map. - return; - } - NavigationServer2D::get_singleton()->free(layers[p_layer].navigation_map); - layers[p_layer].navigation_map = RID(); - } -} - -void TileMap::_rendering_update_layer(int p_layer) { - ERR_FAIL_INDEX(p_layer, (int)layers.size()); - +void TileMapLayer::_rendering_update() { RenderingServer *rs = RenderingServer::get_singleton(); - if (!layers[p_layer].canvas_item.is_valid()) { + if (!canvas_item.is_valid()) { RID ci = rs->canvas_item_create(); - rs->canvas_item_set_parent(ci, get_canvas_item()); - - /*Transform2D xform; - xform.set_origin(Vector2(0, p_layer)); - rs->canvas_item_set_transform(ci, xform);*/ - rs->canvas_item_set_draw_index(ci, p_layer - (int64_t)0x80000000); - - layers[p_layer].canvas_item = ci; - } - RID &ci = layers[p_layer].canvas_item; - rs->canvas_item_set_sort_children_by_y(ci, layers[p_layer].y_sort_enabled); - rs->canvas_item_set_use_parent_material(ci, get_use_parent_material() || get_material().is_valid()); - rs->canvas_item_set_z_index(ci, layers[p_layer].z_index); - rs->canvas_item_set_default_texture_filter(ci, RS::CanvasItemTextureFilter(get_texture_filter_in_tree())); - rs->canvas_item_set_default_texture_repeat(ci, RS::CanvasItemTextureRepeat(get_texture_repeat_in_tree())); - rs->canvas_item_set_light_mask(ci, get_light_mask()); - - Color layer_modulate = get_layer_modulate(p_layer); - if (selected_layer >= 0 && p_layer != selected_layer) { - int z1 = get_layer_z_index(p_layer); - int z2 = get_layer_z_index(selected_layer); - if (z1 < z2 || (z1 == z2 && p_layer < selected_layer)) { + rs->canvas_item_set_parent(ci, tile_map_node->get_canvas_item()); + rs->canvas_item_set_draw_index(ci, layer_index_in_tile_map_node - (int64_t)0x80000000); + canvas_item = ci; + } + RID &ci = canvas_item; + rs->canvas_item_set_sort_children_by_y(ci, y_sort_enabled); + rs->canvas_item_set_use_parent_material(ci, tile_map_node->get_use_parent_material() || tile_map_node->get_material().is_valid()); + rs->canvas_item_set_z_index(ci, z_index); + rs->canvas_item_set_default_texture_filter(ci, RS::CanvasItemTextureFilter(tile_map_node->get_texture_filter_in_tree())); + rs->canvas_item_set_default_texture_repeat(ci, RS::CanvasItemTextureRepeat(tile_map_node->get_texture_repeat_in_tree())); + rs->canvas_item_set_light_mask(ci, tile_map_node->get_light_mask()); + + Color layer_modulate = modulate; + int selected_layer = tile_map_node->get_selected_layer(); + if (selected_layer >= 0 && layer_index_in_tile_map_node != selected_layer) { + int z_selected = tile_map_node->get_layer_z_index(selected_layer); + if (z_index < z_selected || (z_index == z_selected && layer_index_in_tile_map_node < selected_layer)) { layer_modulate = layer_modulate.darkened(0.5); - } else if (z1 > z2 || (z1 == z2 && p_layer > selected_layer)) { + } else if (z_index > z_selected || (z_index == z_selected && layer_index_in_tile_map_node > selected_layer)) { layer_modulate = layer_modulate.darkened(0.5); layer_modulate.a *= 0.3; } @@ -1209,22 +136,21 @@ void TileMap::_rendering_update_layer(int p_layer) { rs->canvas_item_set_modulate(ci, layer_modulate); } -void TileMap::_rendering_cleanup_layer(int p_layer) { - ERR_FAIL_INDEX(p_layer, (int)layers.size()); - +void TileMapLayer::_rendering_cleanup() { ERR_FAIL_NULL(RenderingServer::get_singleton()); RenderingServer *rs = RenderingServer::get_singleton(); - if (layers[p_layer].canvas_item.is_valid()) { - rs->free(layers[p_layer].canvas_item); - layers[p_layer].canvas_item = RID(); + if (canvas_item.is_valid()) { + rs->free(canvas_item); + canvas_item = RID(); } } -void TileMap::_rendering_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) { - ERR_FAIL_COND(!is_inside_tree()); +void TileMapLayer::_rendering_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) { + ERR_FAIL_COND(!tile_map_node->is_inside_tree()); + const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); ERR_FAIL_COND(!tile_set.is_valid()); - bool node_visible = is_visible_in_tree(); + bool node_visible = tile_map_node->is_visible_in_tree(); SelfList<TileMapQuadrant> *q_list_element = r_dirty_quadrant_list.first(); while (q_list_element) { @@ -1251,7 +177,7 @@ void TileMap::_rendering_update_dirty_quadrants(SelfList<TileMapQuadrant>::List // Iterate over the cells of the quadrant. for (const KeyValue<Vector2, Vector2i> &E_cell : q.local_to_map) { - TileMapCell c = get_cell(q.layer, E_cell.value, true); + TileMapCell c = get_cell(E_cell.value, true); TileSetSource *source; if (tile_set->has_source(c.source_id)) { @@ -1275,10 +201,10 @@ void TileMap::_rendering_update_dirty_quadrants(SelfList<TileMapQuadrant>::List int tile_z_index = tile_data->get_z_index(); // Quandrant pos. - Vector2 tile_position = map_to_local(q.coords * get_effective_quadrant_size(q.layer)); - if (is_y_sort_enabled() && layers[q.layer].y_sort_enabled) { + Vector2 tile_position = tile_map_node->map_to_local(q.coords * get_effective_quadrant_size()); + if (tile_map_node->is_y_sort_enabled() && y_sort_enabled) { // When Y-sorting, the quandrant size is sure to be 1, we can thus offset the CanvasItem. - tile_position.y += layers[q.layer].y_sort_origin + tile_data->get_y_sort_origin(); + tile_position.y += y_sort_origin + tile_data->get_y_sort_origin(); } // --- CanvasItems --- @@ -1292,19 +218,19 @@ void TileMap::_rendering_update_dirty_quadrants(SelfList<TileMapQuadrant>::List if (mat.is_valid()) { rs->canvas_item_set_material(ci, mat->get_rid()); } - rs->canvas_item_set_parent(ci, layers[q.layer].canvas_item); - rs->canvas_item_set_use_parent_material(ci, get_use_parent_material() || get_material().is_valid()); + rs->canvas_item_set_parent(ci, canvas_item); + rs->canvas_item_set_use_parent_material(ci, tile_map_node->get_use_parent_material() || tile_map_node->get_material().is_valid()); Transform2D xform; xform.set_origin(tile_position); rs->canvas_item_set_transform(ci, xform); - rs->canvas_item_set_light_mask(ci, get_light_mask()); + rs->canvas_item_set_light_mask(ci, tile_map_node->get_light_mask()); rs->canvas_item_set_z_as_relative_to_parent(ci, true); rs->canvas_item_set_z_index(ci, tile_z_index); - rs->canvas_item_set_default_texture_filter(ci, RS::CanvasItemTextureFilter(get_texture_filter_in_tree())); - rs->canvas_item_set_default_texture_repeat(ci, RS::CanvasItemTextureRepeat(get_texture_repeat_in_tree())); + rs->canvas_item_set_default_texture_filter(ci, RS::CanvasItemTextureFilter(tile_map_node->get_texture_filter_in_tree())); + rs->canvas_item_set_default_texture_repeat(ci, RS::CanvasItemTextureRepeat(tile_map_node->get_texture_repeat_in_tree())); q.canvas_items.push_back(ci); @@ -1317,21 +243,17 @@ void TileMap::_rendering_update_dirty_quadrants(SelfList<TileMapQuadrant>::List ci = prev_ci; } - Vector2 p_position = E_cell.key - tile_position; - Vector2 p_atlas_coords = c.get_atlas_coords(); - // Random animation offset. - real_t p_random_animation_offset = 0.0; - if (atlas_source->get_tile_animation_mode(p_atlas_coords) != TileSetAtlasSource::TILE_ANIMATION_MODE_DEFAULT) { + real_t random_animation_offset = 0.0; + if (atlas_source->get_tile_animation_mode(c.get_atlas_coords()) != TileSetAtlasSource::TILE_ANIMATION_MODE_DEFAULT) { Array to_hash; - to_hash.push_back(p_position); - to_hash.push_back(q.layer); - to_hash.push_back(Variant(this)); - p_random_animation_offset = RandomPCG(to_hash.hash()).randf(); + to_hash.push_back(E_cell.key); + to_hash.push_back(get_instance_id()); // Use instance id as a random hash + random_animation_offset = RandomPCG(to_hash.hash()).randf(); } // Drawing the tile in the canvas item. - draw_tile(ci, p_position, tile_set, c.source_id, p_atlas_coords, c.alternative_tile, -1, get_self_modulate(), tile_data, p_random_animation_offset); + tile_map_node->draw_tile(ci, E_cell.key - tile_position, tile_set, c.source_id, c.get_atlas_coords(), c.alternative_tile, -1, tile_map_node->get_self_modulate(), tile_data, random_animation_offset); // --- Occluders --- for (int i = 0; i < tile_set->get_occlusion_layers_count(); i++) { @@ -1340,9 +262,9 @@ void TileMap::_rendering_update_dirty_quadrants(SelfList<TileMapQuadrant>::List if (tile_data->get_occluder(i).is_valid()) { RID occluder_id = rs->canvas_light_occluder_create(); rs->canvas_light_occluder_set_enabled(occluder_id, node_visible); - rs->canvas_light_occluder_set_transform(occluder_id, get_global_transform() * xform); + rs->canvas_light_occluder_set_transform(occluder_id, tile_map_node->get_global_transform() * xform); rs->canvas_light_occluder_set_polygon(occluder_id, tile_data->get_occluder(i)->get_rid()); - rs->canvas_light_occluder_attach_to_canvas(occluder_id, get_canvas()); + rs->canvas_light_occluder_attach_to_canvas(occluder_id, tile_map_node->get_canvas()); rs->canvas_light_occluder_set_light_mask(occluder_id, tile_set->get_occlusion_layer_light_mask(i)); q.occluders[E_cell.value] = occluder_id; } @@ -1355,36 +277,35 @@ void TileMap::_rendering_update_dirty_quadrants(SelfList<TileMapQuadrant>::List q_list_element = q_list_element->next(); } - // Reset the drawing indices + // Reset the drawing indices. if (_rendering_quadrant_order_dirty) { int index = -(int64_t)0x80000000; //always must be drawn below children. - for (TileMapLayer &layer : layers) { - // Sort the quadrants coords per local coordinates. - RBMap<Vector2, Vector2i, TileMapQuadrant::CoordsWorldComparator> local_to_map; - for (const KeyValue<Vector2i, TileMapQuadrant> &E : layer.quadrant_map) { - local_to_map[map_to_local(E.key)] = E.key; - } + // Sort the quadrants coords per local coordinates. + RBMap<Vector2, Vector2i, TileMapQuadrant::CoordsWorldComparator> local_to_map; + for (const KeyValue<Vector2i, TileMapQuadrant> &E : quadrant_map) { + local_to_map[tile_map_node->map_to_local(E.key)] = E.key; + } - // Sort the quadrants. - for (const KeyValue<Vector2, Vector2i> &E : local_to_map) { - TileMapQuadrant &q = layer.quadrant_map[E.value]; - for (const RID &ci : q.canvas_items) { - RS::get_singleton()->canvas_item_set_draw_index(ci, index++); - } + // Sort the quadrants. + for (const KeyValue<Vector2, Vector2i> &E : local_to_map) { + TileMapQuadrant &q = quadrant_map[E.value]; + for (const RID &ci : q.canvas_items) { + RS::get_singleton()->canvas_item_set_draw_index(ci, index++); } } _rendering_quadrant_order_dirty = false; } } -void TileMap::_rendering_create_quadrant(TileMapQuadrant *p_quadrant) { +void TileMapLayer::_rendering_create_quadrant(TileMapQuadrant *p_quadrant) { + const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); ERR_FAIL_COND(!tile_set.is_valid()); _rendering_quadrant_order_dirty = true; } -void TileMap::_rendering_cleanup_quadrant(TileMapQuadrant *p_quadrant) { +void TileMapLayer::_rendering_cleanup_quadrant(TileMapQuadrant *p_quadrant) { ERR_FAIL_NULL(RenderingServer::get_singleton()); // Free the canvas items. for (const RID &ci : p_quadrant->canvas_items) { @@ -1399,7 +320,8 @@ void TileMap::_rendering_cleanup_quadrant(TileMapQuadrant *p_quadrant) { p_quadrant->occluders.clear(); } -void TileMap::_rendering_draw_quadrant_debug(TileMapQuadrant *p_quadrant) { +void TileMapLayer::_rendering_draw_quadrant_debug(TileMapQuadrant *p_quadrant) { + const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); ERR_FAIL_COND(!tile_set.is_valid()); if (!Engine::get_singleton()->is_editor_hint()) { @@ -1408,9 +330,9 @@ void TileMap::_rendering_draw_quadrant_debug(TileMapQuadrant *p_quadrant) { // Draw a placeholder for tiles needing one. RenderingServer *rs = RenderingServer::get_singleton(); - Vector2 quadrant_pos = map_to_local(p_quadrant->coords * get_effective_quadrant_size(p_quadrant->layer)); + Vector2 quadrant_pos = tile_map_node->map_to_local(p_quadrant->coords * get_effective_quadrant_size()); for (const Vector2i &E_cell : p_quadrant->cells) { - const TileMapCell &c = get_cell(p_quadrant->layer, E_cell, true); + const TileMapCell &c = get_cell(E_cell, true); TileSetSource *source; if (tile_set->has_source(c.source_id)) { @@ -1440,7 +362,7 @@ void TileMap::_rendering_draw_quadrant_debug(TileMapQuadrant *p_quadrant) { // Draw a placeholder tile. Transform2D cell_to_quadrant; - cell_to_quadrant.set_origin(map_to_local(E_cell) - quadrant_pos); + cell_to_quadrant.set_origin(tile_map_node->map_to_local(E_cell) - quadrant_pos); rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, cell_to_quadrant); rs->canvas_item_add_circle(p_quadrant->debug_canvas_item, Vector2(), MIN(tile_set->get_tile_size().x, tile_set->get_tile_size().y) / 4.0, color); } @@ -1449,167 +371,17 @@ void TileMap::_rendering_draw_quadrant_debug(TileMapQuadrant *p_quadrant) { } } -void TileMap::draw_tile(RID p_canvas_item, const Vector2 &p_position, const Ref<TileSet> p_tile_set, int p_atlas_source_id, const Vector2i &p_atlas_coords, int p_alternative_tile, int p_frame, Color p_modulation, const TileData *p_tile_data_override, real_t p_animation_offset) { - ERR_FAIL_COND(!p_tile_set.is_valid()); - ERR_FAIL_COND(!p_tile_set->has_source(p_atlas_source_id)); - ERR_FAIL_COND(!p_tile_set->get_source(p_atlas_source_id)->has_tile(p_atlas_coords)); - ERR_FAIL_COND(!p_tile_set->get_source(p_atlas_source_id)->has_alternative_tile(p_atlas_coords, p_alternative_tile)); - TileSetSource *source = *p_tile_set->get_source(p_atlas_source_id); - TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); - if (atlas_source) { - // Check for the frame. - if (p_frame >= 0) { - ERR_FAIL_INDEX(p_frame, atlas_source->get_tile_animation_frames_count(p_atlas_coords)); - } - - // Get the texture. - Ref<Texture2D> tex = atlas_source->get_runtime_texture(); - if (!tex.is_valid()) { - return; - } - - // Check if we are in the texture, return otherwise. - Vector2i grid_size = atlas_source->get_atlas_grid_size(); - if (p_atlas_coords.x >= grid_size.x || p_atlas_coords.y >= grid_size.y) { - return; - } - - // Get tile data. - const TileData *tile_data = p_tile_data_override ? p_tile_data_override : atlas_source->get_tile_data(p_atlas_coords, p_alternative_tile); - - // Get the tile modulation. - Color modulate = tile_data->get_modulate() * p_modulation; - - // Compute the offset. - Vector2 tile_offset = tile_data->get_texture_origin(); - - // Get destination rect. - Rect2 dest_rect; - dest_rect.size = atlas_source->get_runtime_tile_texture_region(p_atlas_coords).size; - dest_rect.size.x += FP_ADJUST; - dest_rect.size.y += FP_ADJUST; - - bool transpose = tile_data->get_transpose(); - if (transpose) { - dest_rect.position = (p_position - Vector2(dest_rect.size.y, dest_rect.size.x) / 2 - tile_offset); - } else { - dest_rect.position = (p_position - dest_rect.size / 2 - tile_offset); - } - - if (tile_data->get_flip_h()) { - dest_rect.size.x = -dest_rect.size.x; - } - - if (tile_data->get_flip_v()) { - dest_rect.size.y = -dest_rect.size.y; - } - - // Draw the tile. - if (p_frame >= 0) { - Rect2i source_rect = atlas_source->get_runtime_tile_texture_region(p_atlas_coords, p_frame); - tex->draw_rect_region(p_canvas_item, dest_rect, source_rect, modulate, transpose, p_tile_set->is_uv_clipping()); - } else if (atlas_source->get_tile_animation_frames_count(p_atlas_coords) == 1) { - Rect2i source_rect = atlas_source->get_runtime_tile_texture_region(p_atlas_coords, 0); - tex->draw_rect_region(p_canvas_item, dest_rect, source_rect, modulate, transpose, p_tile_set->is_uv_clipping()); - } else { - real_t speed = atlas_source->get_tile_animation_speed(p_atlas_coords); - real_t animation_duration = atlas_source->get_tile_animation_total_duration(p_atlas_coords) / speed; - real_t time = 0.0; - for (int frame = 0; frame < atlas_source->get_tile_animation_frames_count(p_atlas_coords); frame++) { - real_t frame_duration = atlas_source->get_tile_animation_frame_duration(p_atlas_coords, frame) / speed; - RenderingServer::get_singleton()->canvas_item_add_animation_slice(p_canvas_item, animation_duration, time, time + frame_duration, p_animation_offset); - - Rect2i source_rect = atlas_source->get_runtime_tile_texture_region(p_atlas_coords, frame); - tex->draw_rect_region(p_canvas_item, dest_rect, source_rect, modulate, transpose, p_tile_set->is_uv_clipping()); - - time += frame_duration; - } - RenderingServer::get_singleton()->canvas_item_add_animation_slice(p_canvas_item, 1.0, 0.0, 1.0, 0.0); - } - } -} - /////////////////////////////// Physics ////////////////////////////////////// -void TileMap::_physics_notification(int p_what) { - switch (p_what) { - case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { - bool in_editor = false; -#ifdef TOOLS_ENABLED - in_editor = Engine::get_singleton()->is_editor_hint(); -#endif - if (is_inside_tree() && collision_animatable && !in_editor) { - // Update transform on the physics tick when in animatable mode. - last_valid_transform = new_transform; - set_notify_local_transform(false); - set_global_transform(new_transform); - set_notify_local_transform(true); - } - } break; - - case NOTIFICATION_TRANSFORM_CHANGED: { - bool in_editor = false; -#ifdef TOOLS_ENABLED - in_editor = Engine::get_singleton()->is_editor_hint(); -#endif - if (is_inside_tree() && (!collision_animatable || in_editor)) { - // Update the new transform directly if we are not in animatable mode. - Transform2D gl_transform = get_global_transform(); - for (TileMapLayer &layer : layers) { - for (KeyValue<Vector2i, TileMapQuadrant> &E : layer.quadrant_map) { - TileMapQuadrant &q = E.value; - - for (RID body : q.bodies) { - Transform2D xform; - xform.set_origin(map_to_local(bodies_coords[body])); - xform = gl_transform * xform; - PhysicsServer2D::get_singleton()->body_set_state(body, PhysicsServer2D::BODY_STATE_TRANSFORM, xform); - } - } - } - } - } break; - - case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: { - bool in_editor = false; -#ifdef TOOLS_ENABLED - in_editor = Engine::get_singleton()->is_editor_hint(); -#endif - if (is_inside_tree() && !in_editor && collision_animatable) { - // Only active when animatable. Send the new transform to the physics... - new_transform = get_global_transform(); - for (TileMapLayer &layer : layers) { - for (KeyValue<Vector2i, TileMapQuadrant> &E : layer.quadrant_map) { - TileMapQuadrant &q = E.value; - - for (RID body : q.bodies) { - Transform2D xform; - xform.set_origin(map_to_local(bodies_coords[body])); - xform = new_transform * xform; - - PhysicsServer2D::get_singleton()->body_set_state(body, PhysicsServer2D::BODY_STATE_TRANSFORM, xform); - } - } - } - - // ... but then revert changes. - set_notify_local_transform(false); - set_global_transform(last_valid_transform); - set_notify_local_transform(true); - } - } break; - } -} - -void TileMap::_physics_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) { - ERR_FAIL_COND(!is_inside_tree()); +void TileMapLayer::_physics_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) { + ERR_FAIL_COND(!tile_map_node->is_inside_tree()); + const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); ERR_FAIL_COND(!tile_set.is_valid()); - Transform2D gl_transform = get_global_transform(); - last_valid_transform = gl_transform; - new_transform = gl_transform; + Transform2D gl_transform = tile_map_node->get_global_transform(); + PhysicsServer2D *ps = PhysicsServer2D::get_singleton(); - RID space = get_world_2d()->get_space(); + RID space = tile_map_node->get_world_2d()->get_space(); SelfList<TileMapQuadrant> *q_list_element = r_dirty_quadrant_list.first(); while (q_list_element) { @@ -1624,7 +396,7 @@ void TileMap::_physics_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r // Recreate bodies and shapes. for (const Vector2i &E_cell : q.cells) { - TileMapCell c = get_cell(q.layer, E_cell, true); + TileMapCell c = get_cell(E_cell, true); TileSetSource *source; if (tile_set->has_source(c.source_id)) { @@ -1650,16 +422,15 @@ void TileMap::_physics_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r // Create the body. RID body = ps->body_create(); bodies_coords[body] = E_cell; - bodies_layers[body] = q.layer; - ps->body_set_mode(body, collision_animatable ? PhysicsServer2D::BODY_MODE_KINEMATIC : PhysicsServer2D::BODY_MODE_STATIC); + ps->body_set_mode(body, tile_map_node->is_collision_animatable() ? PhysicsServer2D::BODY_MODE_KINEMATIC : PhysicsServer2D::BODY_MODE_STATIC); ps->body_set_space(body, space); Transform2D xform; - xform.set_origin(map_to_local(E_cell)); + xform.set_origin(tile_map_node->map_to_local(E_cell)); xform = gl_transform * xform; ps->body_set_state(body, PhysicsServer2D::BODY_STATE_TRANSFORM, xform); - ps->body_attach_object_instance_id(body, get_instance_id()); + ps->body_attach_object_instance_id(body, tile_map_node->get_instance_id()); ps->body_set_collision_layer(body, physics_layer); ps->body_set_collision_mask(body, physics_mask); ps->body_set_pickable(body, false); @@ -1701,29 +472,29 @@ void TileMap::_physics_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r } } -void TileMap::_physics_cleanup_quadrant(TileMapQuadrant *p_quadrant) { +void TileMapLayer::_physics_cleanup_quadrant(TileMapQuadrant *p_quadrant) { // Remove a quadrant. ERR_FAIL_NULL(PhysicsServer2D::get_singleton()); for (RID body : p_quadrant->bodies) { bodies_coords.erase(body); - bodies_layers.erase(body); PhysicsServer2D::get_singleton()->free(body); } p_quadrant->bodies.clear(); } -void TileMap::_physics_draw_quadrant_debug(TileMapQuadrant *p_quadrant) { +void TileMapLayer::_physics_draw_quadrant_debug(TileMapQuadrant *p_quadrant) { // Draw the debug collision shapes. + const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); ERR_FAIL_COND(!tile_set.is_valid()); - if (!get_tree()) { + if (!tile_map_node->get_tree()) { return; } bool show_collision = false; - switch (collision_visibility_mode) { + switch (tile_map_node->get_collision_visibility_mode()) { case TileMap::VISIBILITY_MODE_DEFAULT: - show_collision = !Engine::get_singleton()->is_editor_hint() && (get_tree() && get_tree()->is_debugging_collisions_hint()); + show_collision = !Engine::get_singleton()->is_editor_hint() && tile_map_node->get_tree()->is_debugging_collisions_hint(); break; case TileMap::VISIBILITY_MODE_FORCE_HIDE: show_collision = false; @@ -1739,14 +510,14 @@ void TileMap::_physics_draw_quadrant_debug(TileMapQuadrant *p_quadrant) { RenderingServer *rs = RenderingServer::get_singleton(); PhysicsServer2D *ps = PhysicsServer2D::get_singleton(); - Color debug_collision_color = get_tree()->get_debug_collisions_color(); + Color debug_collision_color = tile_map_node->get_tree()->get_debug_collisions_color(); Vector<Color> color; color.push_back(debug_collision_color); - Vector2 quadrant_pos = map_to_local(p_quadrant->coords * get_effective_quadrant_size(p_quadrant->layer)); + Vector2 quadrant_pos = tile_map_node->map_to_local(p_quadrant->coords * get_effective_quadrant_size()); Transform2D quadrant_to_local; quadrant_to_local.set_origin(quadrant_pos); - Transform2D global_to_quadrant = (get_global_transform() * quadrant_to_local).affine_inverse(); + Transform2D global_to_quadrant = (tile_map_node->get_global_transform() * quadrant_to_local).affine_inverse(); for (RID body : p_quadrant->bodies) { Transform2D body_to_quadrant = global_to_quadrant * Transform2D(ps->body_get_state(body, PhysicsServer2D::BODY_STATE_TRANSFORM)); @@ -1767,36 +538,12 @@ void TileMap::_physics_draw_quadrant_debug(TileMapQuadrant *p_quadrant) { /////////////////////////////// Navigation ////////////////////////////////////// -void TileMap::_navigation_notification(int p_what) { - switch (p_what) { - case NOTIFICATION_TRANSFORM_CHANGED: { - if (is_inside_tree()) { - for (TileMapLayer &layer : layers) { - Transform2D tilemap_xform = get_global_transform(); - for (KeyValue<Vector2i, TileMapQuadrant> &E_quadrant : layer.quadrant_map) { - TileMapQuadrant &q = E_quadrant.value; - for (const KeyValue<Vector2i, Vector<RID>> &E_region : q.navigation_regions) { - for (const RID ®ion : E_region.value) { - if (!region.is_valid()) { - continue; - } - Transform2D tile_transform; - tile_transform.set_origin(map_to_local(E_region.key)); - NavigationServer2D::get_singleton()->region_set_transform(region, tilemap_xform * tile_transform); - } - } - } - } - } - } break; - } -} - -void TileMap::_navigation_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) { - ERR_FAIL_COND(!is_inside_tree()); +void TileMapLayer::_navigation_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) { + ERR_FAIL_COND(!tile_map_node->is_inside_tree()); + const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); ERR_FAIL_COND(!tile_set.is_valid()); - Transform2D tilemap_xform = get_global_transform(); + Transform2D tilemap_xform = tile_map_node->get_global_transform(); SelfList<TileMapQuadrant> *q_list_element = r_dirty_quadrant_list.first(); while (q_list_element) { TileMapQuadrant &q = *q_list_element->self(); @@ -1815,7 +562,7 @@ void TileMap::_navigation_update_dirty_quadrants(SelfList<TileMapQuadrant>::List // Get the navigation polygons and create regions. for (const Vector2i &E_cell : q.cells) { - TileMapCell c = get_cell(q.layer, E_cell, true); + TileMapCell c = get_cell(E_cell, true); TileSetSource *source; if (tile_set->has_source(c.source_id)) { @@ -1835,24 +582,21 @@ void TileMap::_navigation_update_dirty_quadrants(SelfList<TileMapQuadrant>::List } q.navigation_regions[E_cell].resize(tile_set->get_navigation_layers_count()); - for (int layer_index = 0; layer_index < tile_set->get_navigation_layers_count(); layer_index++) { - if (layer_index >= (int)layers.size() || !layers[layer_index].navigation_map.is_valid()) { - continue; - } + for (int navigation_layer_index = 0; navigation_layer_index < tile_set->get_navigation_layers_count(); navigation_layer_index++) { Ref<NavigationPolygon> navigation_polygon; - navigation_polygon = tile_data->get_navigation_polygon(layer_index); + navigation_polygon = tile_data->get_navigation_polygon(navigation_layer_index); if (navigation_polygon.is_valid()) { Transform2D tile_transform; - tile_transform.set_origin(map_to_local(E_cell)); + tile_transform.set_origin(tile_map_node->map_to_local(E_cell)); RID region = NavigationServer2D::get_singleton()->region_create(); - NavigationServer2D::get_singleton()->region_set_owner_id(region, get_instance_id()); - NavigationServer2D::get_singleton()->region_set_map(region, layers[layer_index].navigation_map); + NavigationServer2D::get_singleton()->region_set_owner_id(region, tile_map_node->get_instance_id()); + NavigationServer2D::get_singleton()->region_set_map(region, navigation_map); NavigationServer2D::get_singleton()->region_set_transform(region, tilemap_xform * tile_transform); - NavigationServer2D::get_singleton()->region_set_navigation_layers(region, tile_set->get_navigation_layer_layers(layer_index)); + NavigationServer2D::get_singleton()->region_set_navigation_layers(region, tile_set->get_navigation_layer_layers(navigation_layer_index)); NavigationServer2D::get_singleton()->region_set_navigation_polygon(region, navigation_polygon); - q.navigation_regions[E_cell].write[layer_index] = region; + q.navigation_regions[E_cell].write[navigation_layer_index] = region; } } } @@ -1863,7 +607,37 @@ void TileMap::_navigation_update_dirty_quadrants(SelfList<TileMapQuadrant>::List } } -void TileMap::_navigation_cleanup_quadrant(TileMapQuadrant *p_quadrant) { +void TileMapLayer::_navigation_update() { + ERR_FAIL_NULL(NavigationServer2D::get_singleton()); + + if (!navigation_map.is_valid()) { + if (layer_index_in_tile_map_node == 0 && tile_map_node->is_inside_tree()) { + // Use the default World2D navigation map for the first layer when empty. + navigation_map = tile_map_node->get_world_2d()->get_navigation_map(); + uses_world_navigation_map = true; + } else { + RID new_layer_map = NavigationServer2D::get_singleton()->map_create(); + NavigationServer2D::get_singleton()->map_set_active(new_layer_map, true); + navigation_map = new_layer_map; + uses_world_navigation_map = false; + } + } +} + +void TileMapLayer::_navigation_cleanup() { + ERR_FAIL_NULL(NavigationServer2D::get_singleton()); + + if (navigation_map.is_valid()) { + if (uses_world_navigation_map) { + // Do not delete the World2D default navigation map. + return; + } + NavigationServer2D::get_singleton()->free(navigation_map); + navigation_map = RID(); + } +} + +void TileMapLayer::_navigation_cleanup_quadrant(TileMapQuadrant *p_quadrant) { // Clear navigation shapes in the quadrant. ERR_FAIL_NULL(NavigationServer2D::get_singleton()); for (const KeyValue<Vector2i, Vector<RID>> &E : p_quadrant->navigation_regions) { @@ -1878,18 +652,19 @@ void TileMap::_navigation_cleanup_quadrant(TileMapQuadrant *p_quadrant) { p_quadrant->navigation_regions.clear(); } -void TileMap::_navigation_draw_quadrant_debug(TileMapQuadrant *p_quadrant) { +void TileMapLayer::_navigation_draw_quadrant_debug(TileMapQuadrant *p_quadrant) { // Draw the debug collision shapes. + const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); ERR_FAIL_COND(!tile_set.is_valid()); - if (!get_tree()) { + if (!tile_map_node->get_tree()) { return; } bool show_navigation = false; - switch (navigation_visibility_mode) { + switch (tile_map_node->get_navigation_visibility_mode()) { case TileMap::VISIBILITY_MODE_DEFAULT: - show_navigation = !Engine::get_singleton()->is_editor_hint() && (get_tree() && get_tree()->is_debugging_navigation_hint()); + show_navigation = !Engine::get_singleton()->is_editor_hint() && tile_map_node->get_tree()->is_debugging_navigation_hint(); break; case TileMap::VISIBILITY_MODE_FORCE_HIDE: show_navigation = false; @@ -1914,10 +689,10 @@ void TileMap::_navigation_draw_quadrant_debug(TileMapQuadrant *p_quadrant) { RandomPCG rand; - Vector2 quadrant_pos = map_to_local(p_quadrant->coords * get_effective_quadrant_size(p_quadrant->layer)); + Vector2 quadrant_pos = tile_map_node->map_to_local(p_quadrant->coords * get_effective_quadrant_size()); for (const Vector2i &E_cell : p_quadrant->cells) { - TileMapCell c = get_cell(p_quadrant->layer, E_cell, true); + TileMapCell c = get_cell(E_cell, true); TileSetSource *source; if (tile_set->has_source(c.source_id)) { @@ -1937,7 +712,7 @@ void TileMap::_navigation_draw_quadrant_debug(TileMapQuadrant *p_quadrant) { } Transform2D cell_to_quadrant; - cell_to_quadrant.set_origin(map_to_local(E_cell) - quadrant_pos); + cell_to_quadrant.set_origin(tile_map_node->map_to_local(E_cell) - quadrant_pos); rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, cell_to_quadrant); for (int layer_index = 0; layer_index < tile_set->get_navigation_layers_count(); layer_index++) { @@ -1989,7 +764,8 @@ void TileMap::_navigation_draw_quadrant_debug(TileMapQuadrant *p_quadrant) { /////////////////////////////// Scenes ////////////////////////////////////// -void TileMap::_scenes_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) { +void TileMapLayer::_scenes_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) { + const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); ERR_FAIL_COND(!tile_set.is_valid()); SelfList<TileMapQuadrant> *q_list_element = r_dirty_quadrant_list.first(); @@ -1999,7 +775,7 @@ void TileMap::_scenes_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_ // Clear the scenes if instance cache was cleared. if (instantiated_scenes.is_empty()) { for (const KeyValue<Vector2i, String> &E : q.scenes) { - Node *node = get_node_or_null(E.value); + Node *node = tile_map_node->get_node_or_null(E.value); if (node) { node->queue_free(); } @@ -2010,16 +786,15 @@ void TileMap::_scenes_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_ // Recreate the scenes. for (const Vector2i &E_cell : q.cells) { - Vector3i cell_coords = Vector3i(q.layer, E_cell.x, E_cell.y); - if (instantiated_scenes.has(cell_coords)) { + if (instantiated_scenes.has(E_cell)) { // Skip scene if the instance was cached (to avoid recreating scenes unnecessarily). continue; } if (!Engine::get_singleton()->is_editor_hint()) { - instantiated_scenes.insert(cell_coords); + instantiated_scenes.insert(E_cell); } - const TileMapCell &c = get_cell(q.layer, E_cell, true); + const TileMapCell &c = get_cell(E_cell, true); TileSetSource *source; if (tile_set->has_source(c.source_id)) { @@ -2037,13 +812,13 @@ void TileMap::_scenes_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_ Control *scene_as_control = Object::cast_to<Control>(scene); Node2D *scene_as_node2d = Object::cast_to<Node2D>(scene); if (scene_as_control) { - scene_as_control->set_position(map_to_local(E_cell) + scene_as_control->get_position()); + scene_as_control->set_position(tile_map_node->map_to_local(E_cell) + scene_as_control->get_position()); } else if (scene_as_node2d) { Transform2D xform; - xform.set_origin(map_to_local(E_cell)); + xform.set_origin(tile_map_node->map_to_local(E_cell)); scene_as_node2d->set_transform(xform * scene_as_node2d->get_transform()); } - add_child(scene); + tile_map_node->add_child(scene); q.scenes[E_cell] = scene->get_name(); } } @@ -2054,11 +829,11 @@ void TileMap::_scenes_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_ } } -void TileMap::_scenes_cleanup_quadrant(TileMapQuadrant *p_quadrant) { +void TileMapLayer::_scenes_cleanup_quadrant(TileMapQuadrant *p_quadrant) { // Clear the scenes if instance cache was cleared. if (instantiated_scenes.is_empty()) { for (const KeyValue<Vector2i, String> &E : p_quadrant->scenes) { - Node *node = get_node_or_null(E.value); + Node *node = tile_map_node->get_node_or_null(E.value); if (node) { node->queue_free(); } @@ -2067,7 +842,8 @@ void TileMap::_scenes_cleanup_quadrant(TileMapQuadrant *p_quadrant) { } } -void TileMap::_scenes_draw_quadrant_debug(TileMapQuadrant *p_quadrant) { +void TileMapLayer::_scenes_draw_quadrant_debug(TileMapQuadrant *p_quadrant) { + const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); ERR_FAIL_COND(!tile_set.is_valid()); if (!Engine::get_singleton()->is_editor_hint()) { @@ -2076,9 +852,9 @@ void TileMap::_scenes_draw_quadrant_debug(TileMapQuadrant *p_quadrant) { // Draw a placeholder for scenes needing one. RenderingServer *rs = RenderingServer::get_singleton(); - Vector2 quadrant_pos = map_to_local(p_quadrant->coords * get_effective_quadrant_size(p_quadrant->layer)); + Vector2 quadrant_pos = tile_map_node->map_to_local(p_quadrant->coords * get_effective_quadrant_size()); for (const Vector2i &E_cell : p_quadrant->cells) { - const TileMapCell &c = get_cell(p_quadrant->layer, E_cell, true); + const TileMapCell &c = get_cell(E_cell, true); TileSetSource *source; if (tile_set->has_source(c.source_id)) { @@ -2106,7 +882,7 @@ void TileMap::_scenes_draw_quadrant_debug(TileMapQuadrant *p_quadrant) { // Draw a placeholder tile. Transform2D cell_to_quadrant; - cell_to_quadrant.set_origin(map_to_local(E_cell) - quadrant_pos); + cell_to_quadrant.set_origin(tile_map_node->map_to_local(E_cell) - quadrant_pos); rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, cell_to_quadrant); rs->canvas_item_add_circle(p_quadrant->debug_canvas_item, Vector2(), MIN(tile_set->get_tile_size().x, tile_set->get_tile_size().y) / 4.0, color); } @@ -2115,249 +891,51 @@ void TileMap::_scenes_draw_quadrant_debug(TileMapQuadrant *p_quadrant) { } } -void TileMap::set_cell(int p_layer, const Vector2i &p_coords, int p_source_id, const Vector2i p_atlas_coords, int p_alternative_tile) { - ERR_FAIL_INDEX(p_layer, (int)layers.size()); - - // Set the current cell tile (using integer position). - HashMap<Vector2i, TileMapCell> &tile_map = layers[p_layer].tile_map; - Vector2i pk(p_coords); - HashMap<Vector2i, TileMapCell>::Iterator E = tile_map.find(pk); - - int source_id = p_source_id; - Vector2i atlas_coords = p_atlas_coords; - int alternative_tile = p_alternative_tile; - - if ((source_id == TileSet::INVALID_SOURCE || atlas_coords == TileSetSource::INVALID_ATLAS_COORDS || alternative_tile == TileSetSource::INVALID_TILE_ALTERNATIVE) && - (source_id != TileSet::INVALID_SOURCE || atlas_coords != TileSetSource::INVALID_ATLAS_COORDS || alternative_tile != TileSetSource::INVALID_TILE_ALTERNATIVE)) { - source_id = TileSet::INVALID_SOURCE; - atlas_coords = TileSetSource::INVALID_ATLAS_COORDS; - alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE; - } - - if (!E && source_id == TileSet::INVALID_SOURCE) { - return; // Nothing to do, the tile is already empty. - } - - // Get the quadrant - Vector2i qk = _coords_to_quadrant_coords(p_layer, pk); - - HashMap<Vector2i, TileMapQuadrant>::Iterator Q = layers[p_layer].quadrant_map.find(qk); - - if (source_id == TileSet::INVALID_SOURCE) { - // Erase existing cell in the tile map. - tile_map.erase(pk); - - // Erase existing cell in the quadrant. - ERR_FAIL_COND(!Q); - TileMapQuadrant &q = Q->value; - - q.cells.erase(pk); - - // Remove or make the quadrant dirty. - if (q.cells.size() == 0) { - _erase_quadrant(Q); - } else { - _make_quadrant_dirty(Q); - } - - used_rect_cache_dirty = true; - } else { - if (!E) { - // Insert a new cell in the tile map. - E = tile_map.insert(pk, TileMapCell()); - - // Create a new quadrant if needed, then insert the cell if needed. - if (!Q) { - Q = _create_quadrant(p_layer, qk); - } - TileMapQuadrant &q = Q->value; - q.cells.insert(pk); - - } else { - ERR_FAIL_COND(!Q); // TileMapQuadrant should exist... - - if (E->value.source_id == source_id && E->value.get_atlas_coords() == atlas_coords && E->value.alternative_tile == alternative_tile) { - return; // Nothing changed. - } - } - - TileMapCell &c = E->value; - - c.source_id = source_id; - c.set_atlas_coords(atlas_coords); - c.alternative_tile = alternative_tile; - - _make_quadrant_dirty(Q); - used_rect_cache_dirty = true; - } -} - -void TileMap::erase_cell(int p_layer, const Vector2i &p_coords) { - set_cell(p_layer, p_coords, TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE); -} - -int TileMap::get_cell_source_id(int p_layer, const Vector2i &p_coords, bool p_use_proxies) const { - ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), TileSet::INVALID_SOURCE); - - // Get a cell source id from position. - const HashMap<Vector2i, TileMapCell> &tile_map = layers[p_layer].tile_map; - HashMap<Vector2i, TileMapCell>::ConstIterator E = tile_map.find(p_coords); - - if (!E) { - return TileSet::INVALID_SOURCE; - } - - if (p_use_proxies && tile_set.is_valid()) { - Array proxyed = tile_set->map_tile_proxy(E->value.source_id, E->value.get_atlas_coords(), E->value.alternative_tile); - return proxyed[0]; - } - - return E->value.source_id; -} - -Vector2i TileMap::get_cell_atlas_coords(int p_layer, const Vector2i &p_coords, bool p_use_proxies) const { - ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), TileSetSource::INVALID_ATLAS_COORDS); - - // Get a cell source id from position - const HashMap<Vector2i, TileMapCell> &tile_map = layers[p_layer].tile_map; - HashMap<Vector2i, TileMapCell>::ConstIterator E = tile_map.find(p_coords); - - if (!E) { - return TileSetSource::INVALID_ATLAS_COORDS; - } - - if (p_use_proxies && tile_set.is_valid()) { - Array proxyed = tile_set->map_tile_proxy(E->value.source_id, E->value.get_atlas_coords(), E->value.alternative_tile); - return proxyed[1]; - } - - return E->value.get_atlas_coords(); -} - -int TileMap::get_cell_alternative_tile(int p_layer, const Vector2i &p_coords, bool p_use_proxies) const { - ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), TileSetSource::INVALID_TILE_ALTERNATIVE); - - // Get a cell source id from position - const HashMap<Vector2i, TileMapCell> &tile_map = layers[p_layer].tile_map; - HashMap<Vector2i, TileMapCell>::ConstIterator E = tile_map.find(p_coords); - - if (!E) { - return TileSetSource::INVALID_TILE_ALTERNATIVE; - } - - if (p_use_proxies && tile_set.is_valid()) { - Array proxyed = tile_set->map_tile_proxy(E->value.source_id, E->value.get_atlas_coords(), E->value.alternative_tile); - return proxyed[2]; - } - - return E->value.alternative_tile; -} +///////////////////////////////////////////////////////////////////// -TileData *TileMap::get_cell_tile_data(int p_layer, const Vector2i &p_coords, bool p_use_proxies) const { - int source_id = get_cell_source_id(p_layer, p_coords, p_use_proxies); - if (source_id == TileSet::INVALID_SOURCE) { - return nullptr; +void TileMapLayer::_build_runtime_update_tile_data(SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) { + if (!tile_map_node->GDVIRTUAL_IS_OVERRIDDEN(_use_tile_data_runtime_update) || !tile_map_node->GDVIRTUAL_IS_OVERRIDDEN(_tile_data_runtime_update)) { + return; } - Ref<TileSetAtlasSource> source = tile_set->get_source(source_id); - if (source.is_valid()) { - return source->get_tile_data(get_cell_atlas_coords(p_layer, p_coords, p_use_proxies), get_cell_alternative_tile(p_layer, p_coords, p_use_proxies)); - } + const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); + SelfList<TileMapQuadrant> *q_list_element = r_dirty_quadrant_list.first(); + while (q_list_element) { + TileMapQuadrant &q = *q_list_element->self(); + // Iterate over the cells of the quadrant. + for (const KeyValue<Vector2, Vector2i> &E_cell : q.local_to_map) { + TileMapCell c = get_cell(E_cell.value, true); - return nullptr; -} + TileSetSource *source; + if (tile_set->has_source(c.source_id)) { + source = *tile_set->get_source(c.source_id); -Ref<TileMapPattern> TileMap::get_pattern(int p_layer, TypedArray<Vector2i> p_coords_array) { - ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), nullptr); - ERR_FAIL_COND_V(!tile_set.is_valid(), nullptr); + if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) { + continue; + } - Ref<TileMapPattern> output; - output.instantiate(); - if (p_coords_array.is_empty()) { - return output; - } + TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); + if (atlas_source) { + bool ret = false; + if (tile_map_node->GDVIRTUAL_CALL(_use_tile_data_runtime_update, layer_index_in_tile_map_node, E_cell.value, ret) && ret) { + TileData *tile_data = atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile); - Vector2i min = Vector2i(p_coords_array[0]); - for (int i = 1; i < p_coords_array.size(); i++) { - min = min.min(p_coords_array[i]); - } + // Create the runtime TileData. + TileData *tile_data_runtime_use = tile_data->duplicate(); + tile_data_runtime_use->set_allow_transform(true); + q.runtime_tile_data_cache[E_cell.value] = tile_data_runtime_use; - Vector<Vector2i> coords_in_pattern_array; - coords_in_pattern_array.resize(p_coords_array.size()); - Vector2i ensure_positive_offset; - for (int i = 0; i < p_coords_array.size(); i++) { - Vector2i coords = p_coords_array[i]; - Vector2i coords_in_pattern = coords - min; - if (tile_set->get_tile_shape() != TileSet::TILE_SHAPE_SQUARE) { - if (tile_set->get_tile_layout() == TileSet::TILE_LAYOUT_STACKED) { - if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_HORIZONTAL && bool(min.y % 2) && bool(coords_in_pattern.y % 2)) { - coords_in_pattern.x -= 1; - if (coords_in_pattern.x < 0) { - ensure_positive_offset.x = 1; + tile_map_node->GDVIRTUAL_CALL(_tile_data_runtime_update, layer_index_in_tile_map_node, E_cell.value, tile_data_runtime_use); } - } else if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_VERTICAL && bool(min.x % 2) && bool(coords_in_pattern.x % 2)) { - coords_in_pattern.y -= 1; - if (coords_in_pattern.y < 0) { - ensure_positive_offset.y = 1; - } - } - } else if (tile_set->get_tile_layout() == TileSet::TILE_LAYOUT_STACKED_OFFSET) { - if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_HORIZONTAL && bool(min.y % 2) && bool(coords_in_pattern.y % 2)) { - coords_in_pattern.x += 1; - } else if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_VERTICAL && bool(min.x % 2) && bool(coords_in_pattern.x % 2)) { - coords_in_pattern.y += 1; } } } - coords_in_pattern_array.write[i] = coords_in_pattern; - } - - for (int i = 0; i < coords_in_pattern_array.size(); i++) { - Vector2i coords = p_coords_array[i]; - Vector2i coords_in_pattern = coords_in_pattern_array[i]; - output->set_cell(coords_in_pattern + ensure_positive_offset, get_cell_source_id(p_layer, coords), get_cell_atlas_coords(p_layer, coords), get_cell_alternative_tile(p_layer, coords)); - } - - return output; -} - -Vector2i TileMap::map_pattern(const Vector2i &p_position_in_tilemap, const Vector2i &p_coords_in_pattern, Ref<TileMapPattern> p_pattern) { - ERR_FAIL_COND_V(p_pattern.is_null(), Vector2i()); - ERR_FAIL_COND_V(!p_pattern->has_cell(p_coords_in_pattern), Vector2i()); - - Vector2i output = p_position_in_tilemap + p_coords_in_pattern; - if (tile_set->get_tile_shape() != TileSet::TILE_SHAPE_SQUARE) { - if (tile_set->get_tile_layout() == TileSet::TILE_LAYOUT_STACKED) { - if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_HORIZONTAL && bool(p_position_in_tilemap.y % 2) && bool(p_coords_in_pattern.y % 2)) { - output.x += 1; - } else if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_VERTICAL && bool(p_position_in_tilemap.x % 2) && bool(p_coords_in_pattern.x % 2)) { - output.y += 1; - } - } else if (tile_set->get_tile_layout() == TileSet::TILE_LAYOUT_STACKED_OFFSET) { - if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_HORIZONTAL && bool(p_position_in_tilemap.y % 2) && bool(p_coords_in_pattern.y % 2)) { - output.x -= 1; - } else if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_VERTICAL && bool(p_position_in_tilemap.x % 2) && bool(p_coords_in_pattern.x % 2)) { - output.y -= 1; - } - } - } - - return output; -} - -void TileMap::set_pattern(int p_layer, const Vector2i &p_position, const Ref<TileMapPattern> p_pattern) { - ERR_FAIL_INDEX(p_layer, (int)layers.size()); - ERR_FAIL_COND(tile_set.is_null()); - ERR_FAIL_COND(p_pattern.is_null()); - - TypedArray<Vector2i> used_cells = p_pattern->get_used_cells(); - for (int i = 0; i < used_cells.size(); i++) { - Vector2i coords = map_pattern(p_position, used_cells[i], p_pattern); - set_cell(p_layer, coords, p_pattern->get_cell_source_id(used_cells[i]), p_pattern->get_cell_atlas_coords(used_cells[i]), p_pattern->get_cell_alternative_tile(used_cells[i])); + q_list_element = q_list_element->next(); } } -TileSet::TerrainsPattern TileMap::_get_best_terrain_pattern_for_constraints(int p_terrain_set, const Vector2i &p_position, const RBSet<TerrainConstraint> &p_constraints, TileSet::TerrainsPattern p_current_pattern) { +TileSet::TerrainsPattern TileMapLayer::_get_best_terrain_pattern_for_constraints(int p_terrain_set, const Vector2i &p_position, const RBSet<TerrainConstraint> &p_constraints, TileSet::TerrainsPattern p_current_pattern) { + const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); if (!tile_set.is_valid()) { return TileSet::TerrainsPattern(); } @@ -2368,8 +946,8 @@ TileSet::TerrainsPattern TileMap::_get_best_terrain_pattern_for_constraints(int for (TileSet::TerrainsPattern &terrain_pattern : pattern_set) { int score = 0; - // Check the center bit constraint - TerrainConstraint terrain_constraint = TerrainConstraint(this, p_position, terrain_pattern.get_terrain()); + // Check the center bit constraint. + TerrainConstraint terrain_constraint = TerrainConstraint(tile_map_node, p_position, terrain_pattern.get_terrain()); const RBSet<TerrainConstraint>::Element *in_set_constraint_element = p_constraints.find(terrain_constraint); if (in_set_constraint_element) { if (in_set_constraint_element->get().get_terrain() != terrain_constraint.get_terrain()) { @@ -2385,7 +963,7 @@ TileSet::TerrainsPattern TileMap::_get_best_terrain_pattern_for_constraints(int TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); if (tile_set->is_valid_terrain_peering_bit(p_terrain_set, bit)) { // Check if the bit is compatible with the constraints. - TerrainConstraint terrain_bit_constraint = TerrainConstraint(this, p_position, bit, terrain_pattern.get_terrain_peering_bit(bit)); + TerrainConstraint terrain_bit_constraint = TerrainConstraint(tile_map_node, p_position, bit, terrain_pattern.get_terrain_peering_bit(bit)); in_set_constraint_element = p_constraints.find(terrain_bit_constraint); if (in_set_constraint_element) { if (in_set_constraint_element->get().get_terrain() != terrain_bit_constraint.get_terrain()) { @@ -2404,7 +982,7 @@ TileSet::TerrainsPattern TileMap::_get_best_terrain_pattern_for_constraints(int terrain_pattern_score[terrain_pattern] = score; } - // Compute the minimum score + // Compute the minimum score. TileSet::TerrainsPattern min_score_pattern = p_current_pattern; int min_score = INT32_MAX; for (KeyValue<TileSet::TerrainsPattern, int> E : terrain_pattern_score) { @@ -2417,19 +995,20 @@ TileSet::TerrainsPattern TileMap::_get_best_terrain_pattern_for_constraints(int return min_score_pattern; } -RBSet<TileMap::TerrainConstraint> TileMap::_get_terrain_constraints_from_added_pattern(const Vector2i &p_position, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern) const { +RBSet<TerrainConstraint> TileMapLayer::_get_terrain_constraints_from_added_pattern(const Vector2i &p_position, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern) const { + const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); if (!tile_set.is_valid()) { return RBSet<TerrainConstraint>(); } // Compute the constraints needed from the surrounding tiles. RBSet<TerrainConstraint> output; - output.insert(TerrainConstraint(this, p_position, p_terrains_pattern.get_terrain())); + output.insert(TerrainConstraint(tile_map_node, p_position, p_terrains_pattern.get_terrain())); for (uint32_t i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { TileSet::CellNeighbor side = TileSet::CellNeighbor(i); if (tile_set->is_valid_terrain_peering_bit(p_terrain_set, side)) { - TerrainConstraint c = TerrainConstraint(this, p_position, side, p_terrains_pattern.get_terrain_peering_bit(side)); + TerrainConstraint c = TerrainConstraint(tile_map_node, p_position, side, p_terrains_pattern.get_terrain_peering_bit(side)); output.insert(c); } } @@ -2437,13 +1016,13 @@ RBSet<TileMap::TerrainConstraint> TileMap::_get_terrain_constraints_from_added_p return output; } -RBSet<TileMap::TerrainConstraint> TileMap::_get_terrain_constraints_from_painted_cells_list(int p_layer, const RBSet<Vector2i> &p_painted, int p_terrain_set, bool p_ignore_empty_terrains) const { +RBSet<TerrainConstraint> TileMapLayer::_get_terrain_constraints_from_painted_cells_list(const RBSet<Vector2i> &p_painted, int p_terrain_set, bool p_ignore_empty_terrains) const { + const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); if (!tile_set.is_valid()) { return RBSet<TerrainConstraint>(); } ERR_FAIL_INDEX_V(p_terrain_set, tile_set->get_terrain_sets_count(), RBSet<TerrainConstraint>()); - ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), RBSet<TerrainConstraint>()); // Build a set of dummy constraints to get the constrained points. RBSet<TerrainConstraint> dummy_constraints; @@ -2451,7 +1030,7 @@ RBSet<TileMap::TerrainConstraint> TileMap::_get_terrain_constraints_from_painted for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { // Iterates over neighbor bits. TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); if (tile_set->is_valid_terrain_peering_bit(p_terrain_set, bit)) { - dummy_constraints.insert(TerrainConstraint(this, E, bit, -1)); + dummy_constraints.insert(TerrainConstraint(tile_map_node, E, bit, -1)); } } } @@ -2465,7 +1044,7 @@ RBSet<TileMap::TerrainConstraint> TileMap::_get_terrain_constraints_from_painted HashMap<Vector2i, TileSet::CellNeighbor> overlapping_terrain_bits = E_constraint.get_overlapping_coords_and_peering_bits(); for (const KeyValue<Vector2i, TileSet::CellNeighbor> &E_overlapping : overlapping_terrain_bits) { TileData *neighbor_tile_data = nullptr; - TileMapCell neighbor_cell = get_cell(p_layer, E_overlapping.key); + TileMapCell neighbor_cell = get_cell(E_overlapping.key); if (neighbor_cell.source_id != TileSet::INVALID_SOURCE) { Ref<TileSetSource> source = tile_set->get_source(neighbor_cell.source_id); Ref<TileSetAtlasSource> atlas_source = source; @@ -2504,10 +1083,10 @@ RBSet<TileMap::TerrainConstraint> TileMap::_get_terrain_constraints_from_painted } } - // Add the centers as constraints + // Add the centers as constraints. for (Vector2i E_coords : p_painted) { TileData *tile_data = nullptr; - TileMapCell cell = get_cell(p_layer, E_coords); + TileMapCell cell = get_cell(E_coords); if (cell.source_id != TileSet::INVALID_SOURCE) { Ref<TileSetSource> source = tile_set->get_source(cell.source_id); Ref<TileSetAtlasSource> atlas_source = source; @@ -2518,14 +1097,54 @@ RBSet<TileMap::TerrainConstraint> TileMap::_get_terrain_constraints_from_painted int terrain = (tile_data && tile_data->get_terrain_set() == p_terrain_set) ? tile_data->get_terrain() : -1; if (!p_ignore_empty_terrains || terrain >= 0) { - constraints.insert(TerrainConstraint(this, E_coords, terrain)); + constraints.insert(TerrainConstraint(tile_map_node, E_coords, terrain)); } } return constraints; } -HashMap<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_fill_constraints(int p_layer, const Vector<Vector2i> &p_to_replace, int p_terrain_set, const RBSet<TerrainConstraint> &p_constraints) { +void TileMapLayer::set_tile_map(TileMap *p_tile_map) { + tile_map_node = p_tile_map; +} + +void TileMapLayer::set_layer_index_in_tile_map_node(int p_index) { + layer_index_in_tile_map_node = p_index; +} + +Rect2 TileMapLayer::get_rect(bool &r_changed) const { + // Compute the displayed area of the tilemap. + r_changed = false; +#ifdef DEBUG_ENABLED + + if (rect_cache_dirty) { + Rect2 r_total; + bool first = true; + for (const KeyValue<Vector2i, TileMapQuadrant> &E : quadrant_map) { + Rect2 r; + r.position = tile_map_node->map_to_local(E.key * get_effective_quadrant_size()); + r.expand_to(tile_map_node->map_to_local((E.key + Vector2i(1, 0)) * get_effective_quadrant_size())); + r.expand_to(tile_map_node->map_to_local((E.key + Vector2i(1, 1)) * get_effective_quadrant_size())); + r.expand_to(tile_map_node->map_to_local((E.key + Vector2i(0, 1)) * get_effective_quadrant_size())); + if (first) { + r_total = r; + first = false; + } else { + r_total = r_total.merge(r); + } + } + + r_changed = rect_cache != r_total; + + rect_cache = r_total; + rect_cache_dirty = false; + } +#endif + return rect_cache; +} + +HashMap<Vector2i, TileSet::TerrainsPattern> TileMapLayer::terrain_fill_constraints(const Vector<Vector2i> &p_to_replace, int p_terrain_set, const RBSet<TerrainConstraint> &p_constraints) { + const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); if (!tile_set.is_valid()) { return HashMap<Vector2i, TileSet::TerrainsPattern>(); } @@ -2540,9 +1159,9 @@ HashMap<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_fill_constraints(in for (int i = 0; i < p_to_replace.size(); i++) { const Vector2i &coords = p_to_replace[i]; - // Select the best pattern for the given constraints + // Select the best pattern for the given constraints. TileSet::TerrainsPattern current_pattern = TileSet::TerrainsPattern(*tile_set, p_terrain_set); - TileMapCell cell = get_cell(p_layer, coords); + TileMapCell cell = get_cell(coords); if (cell.source_id != TileSet::INVALID_SOURCE) { TileSetSource *source = *tile_set->get_source(cell.source_id); TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); @@ -2556,7 +1175,7 @@ HashMap<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_fill_constraints(in } TileSet::TerrainsPattern pattern = _get_best_terrain_pattern_for_constraints(p_terrain_set, coords, constraints, current_pattern); - // Update the constraint set with the new ones + // Update the constraint set with the new ones. RBSet<TerrainConstraint> new_constraints = _get_terrain_constraints_from_added_pattern(coords, p_terrain_set, pattern); for (const TerrainConstraint &E_constraint : new_constraints) { if (constraints.has(E_constraint)) { @@ -2572,12 +1191,13 @@ HashMap<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_fill_constraints(in return output; } -HashMap<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_fill_connect(int p_layer, const Vector<Vector2i> &p_coords_array, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains) { +HashMap<Vector2i, TileSet::TerrainsPattern> TileMapLayer::terrain_fill_connect(const Vector<Vector2i> &p_coords_array, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains) { HashMap<Vector2i, TileSet::TerrainsPattern> output; + const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); ERR_FAIL_COND_V(!tile_set.is_valid(), output); ERR_FAIL_INDEX_V(p_terrain_set, tile_set->get_terrain_sets_count(), output); - // Build list and set of tiles that can be modified (painted and their surroundings) + // Build list and set of tiles that can be modified (painted and their surroundings). Vector<Vector2i> can_modify_list; RBSet<Vector2i> can_modify_set; RBSet<Vector2i> painted_set; @@ -2588,11 +1208,11 @@ HashMap<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_fill_connect(int p_ painted_set.insert(coords); } for (Vector2i coords : p_coords_array) { - // Find the adequate neighbor + // Find the adequate neighbor. for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) { TileSet::CellNeighbor bit = TileSet::CellNeighbor(j); - if (is_existing_neighbor(bit)) { - Vector2i neighbor = get_neighbor_cell(coords, bit); + if (tile_map_node->is_existing_neighbor(bit)) { + Vector2i neighbor = tile_map_node->get_neighbor_cell(coords, bit); if (!can_modify_set.has(neighbor)) { can_modify_list.push_back(neighbor); can_modify_set.insert(neighbor); @@ -2601,16 +1221,16 @@ HashMap<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_fill_connect(int p_ } } - // Build a set, out of the possibly modified tiles, of the one with a center bit that is set (or will be) to the painted terrain + // Build a set, out of the possibly modified tiles, of the one with a center bit that is set (or will be) to the painted terrain. RBSet<Vector2i> cells_with_terrain_center_bit; for (Vector2i coords : can_modify_set) { bool connect = false; if (painted_set.has(coords)) { connect = true; } else { - // Get the center bit of the cell + // Get the center bit of the cell. TileData *tile_data = nullptr; - TileMapCell cell = get_cell(p_layer, coords); + TileMapCell cell = get_cell(coords); if (cell.source_id != TileSet::INVALID_SOURCE) { Ref<TileSetSource> source = tile_set->get_source(cell.source_id); Ref<TileSetAtlasSource> atlas_source = source; @@ -2633,7 +1253,7 @@ HashMap<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_fill_connect(int p_ // Add new constraints from the path drawn. for (Vector2i coords : p_coords_array) { // Constraints on the center bit. - TerrainConstraint c = TerrainConstraint(this, coords, p_terrain); + TerrainConstraint c = TerrainConstraint(tile_map_node, coords, p_terrain); c.set_priority(10); constraints.insert(c); @@ -2641,16 +1261,16 @@ HashMap<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_fill_connect(int p_ for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) { TileSet::CellNeighbor bit = TileSet::CellNeighbor(j); if (tile_set->is_valid_terrain_peering_bit(p_terrain_set, bit)) { - c = TerrainConstraint(this, coords, bit, p_terrain); + c = TerrainConstraint(tile_map_node, coords, bit, p_terrain); c.set_priority(10); if ((int(bit) % 2) == 0) { - // Side peering bits: add the constraint if the center is of the same terrain - Vector2i neighbor = get_neighbor_cell(coords, bit); + // Side peering bits: add the constraint if the center is of the same terrain. + Vector2i neighbor = tile_map_node->get_neighbor_cell(coords, bit); if (cells_with_terrain_center_bit.has(neighbor)) { constraints.insert(c); } } else { - // Corner peering bits: add the constraint if all tiles on the constraint has the same center bit + // Corner peering bits: add the constraint if all tiles on the constraint has the same center bit. HashMap<Vector2i, TileSet::CellNeighbor> overlapping_terrain_bits = c.get_overlapping_coords_and_peering_bits(); bool valid = true; for (KeyValue<Vector2i, TileSet::CellNeighbor> kv : overlapping_terrain_bits) { @@ -2668,54 +1288,55 @@ HashMap<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_fill_connect(int p_ } // Fills in the constraint list from existing tiles. - for (TerrainConstraint c : _get_terrain_constraints_from_painted_cells_list(p_layer, painted_set, p_terrain_set, p_ignore_empty_terrains)) { + for (TerrainConstraint c : _get_terrain_constraints_from_painted_cells_list(painted_set, p_terrain_set, p_ignore_empty_terrains)) { constraints.insert(c); } // Fill the terrains. - output = terrain_fill_constraints(p_layer, can_modify_list, p_terrain_set, constraints); + output = terrain_fill_constraints(can_modify_list, p_terrain_set, constraints); return output; } -HashMap<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_fill_path(int p_layer, const Vector<Vector2i> &p_path, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains) { +HashMap<Vector2i, TileSet::TerrainsPattern> TileMapLayer::terrain_fill_path(const Vector<Vector2i> &p_coords_array, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains) { HashMap<Vector2i, TileSet::TerrainsPattern> output; + const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); ERR_FAIL_COND_V(!tile_set.is_valid(), output); ERR_FAIL_INDEX_V(p_terrain_set, tile_set->get_terrain_sets_count(), output); // Make sure the path is correct and build the peering bit list while doing it. Vector<TileSet::CellNeighbor> neighbor_list; - for (int i = 0; i < p_path.size() - 1; i++) { - // Find the adequate neighbor + for (int i = 0; i < p_coords_array.size() - 1; i++) { + // Find the adequate neighbor. TileSet::CellNeighbor found_bit = TileSet::CELL_NEIGHBOR_MAX; for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) { TileSet::CellNeighbor bit = TileSet::CellNeighbor(j); - if (is_existing_neighbor(bit)) { - if (get_neighbor_cell(p_path[i], bit) == p_path[i + 1]) { + if (tile_map_node->is_existing_neighbor(bit)) { + if (tile_map_node->get_neighbor_cell(p_coords_array[i], bit) == p_coords_array[i + 1]) { found_bit = bit; break; } } } - ERR_FAIL_COND_V_MSG(found_bit == TileSet::CELL_NEIGHBOR_MAX, output, vformat("Invalid terrain path, %s is not a neighboring tile of %s", p_path[i + 1], p_path[i])); + ERR_FAIL_COND_V_MSG(found_bit == TileSet::CELL_NEIGHBOR_MAX, output, vformat("Invalid terrain path, %s is not a neighboring tile of %s", p_coords_array[i + 1], p_coords_array[i])); neighbor_list.push_back(found_bit); } - // Build list and set of tiles that can be modified (painted and their surroundings) + // Build list and set of tiles that can be modified (painted and their surroundings). Vector<Vector2i> can_modify_list; RBSet<Vector2i> can_modify_set; RBSet<Vector2i> painted_set; - for (int i = p_path.size() - 1; i >= 0; i--) { - const Vector2i &coords = p_path[i]; + for (int i = p_coords_array.size() - 1; i >= 0; i--) { + const Vector2i &coords = p_coords_array[i]; can_modify_list.push_back(coords); can_modify_set.insert(coords); painted_set.insert(coords); } - for (Vector2i coords : p_path) { - // Find the adequate neighbor + for (Vector2i coords : p_coords_array) { + // Find the adequate neighbor. for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) { TileSet::CellNeighbor bit = TileSet::CellNeighbor(j); if (tile_set->is_valid_terrain_peering_bit(p_terrain_set, bit)) { - Vector2i neighbor = get_neighbor_cell(coords, bit); + Vector2i neighbor = tile_map_node->get_neighbor_cell(coords, bit); if (!can_modify_set.has(neighbor)) { can_modify_list.push_back(neighbor); can_modify_set.insert(neighbor); @@ -2727,31 +1348,32 @@ HashMap<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_fill_path(int p_lay RBSet<TerrainConstraint> constraints; // Add new constraints from the path drawn. - for (Vector2i coords : p_path) { - // Constraints on the center bit - TerrainConstraint c = TerrainConstraint(this, coords, p_terrain); + for (Vector2i coords : p_coords_array) { + // Constraints on the center bit. + TerrainConstraint c = TerrainConstraint(tile_map_node, coords, p_terrain); c.set_priority(10); constraints.insert(c); } - for (int i = 0; i < p_path.size() - 1; i++) { + for (int i = 0; i < p_coords_array.size() - 1; i++) { // Constraints on the peering bits. - TerrainConstraint c = TerrainConstraint(this, p_path[i], neighbor_list[i], p_terrain); + TerrainConstraint c = TerrainConstraint(tile_map_node, p_coords_array[i], neighbor_list[i], p_terrain); c.set_priority(10); constraints.insert(c); } // Fills in the constraint list from existing tiles. - for (TerrainConstraint c : _get_terrain_constraints_from_painted_cells_list(p_layer, painted_set, p_terrain_set, p_ignore_empty_terrains)) { + for (TerrainConstraint c : _get_terrain_constraints_from_painted_cells_list(painted_set, p_terrain_set, p_ignore_empty_terrains)) { constraints.insert(c); } // Fill the terrains. - output = terrain_fill_constraints(p_layer, can_modify_list, p_terrain_set, constraints); + output = terrain_fill_constraints(can_modify_list, p_terrain_set, constraints); return output; } -HashMap<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_fill_pattern(int p_layer, const Vector<Vector2i> &p_coords_array, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern, bool p_ignore_empty_terrains) { +HashMap<Vector2i, TileSet::TerrainsPattern> TileMapLayer::terrain_fill_pattern(const Vector<Vector2i> &p_coords_array, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern, bool p_ignore_empty_terrains) { HashMap<Vector2i, TileSet::TerrainsPattern> output; + const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); ERR_FAIL_COND_V(!tile_set.is_valid(), output); ERR_FAIL_INDEX_V(p_terrain_set, tile_set->get_terrain_sets_count(), output); @@ -2766,11 +1388,11 @@ HashMap<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_fill_pattern(int p_ painted_set.insert(coords); } for (Vector2i coords : p_coords_array) { - // Find the adequate neighbor + // Find the adequate neighbor. for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) { TileSet::CellNeighbor bit = TileSet::CellNeighbor(j); if (tile_set->is_valid_terrain_peering_bit(p_terrain_set, bit)) { - Vector2i neighbor = get_neighbor_cell(coords, bit); + Vector2i neighbor = tile_map_node->get_neighbor_cell(coords, bit); if (!can_modify_set.has(neighbor)) { can_modify_list.push_back(neighbor); can_modify_set.insert(neighbor); @@ -2784,7 +1406,7 @@ HashMap<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_fill_pattern(int p_ // Add new constraints from the path drawn. for (Vector2i coords : p_coords_array) { - // Constraints on the center bit + // Constraints on the center bit. RBSet<TerrainConstraint> added_constraints = _get_terrain_constraints_from_added_pattern(coords, p_terrain_set, p_terrains_pattern); for (TerrainConstraint c : added_constraints) { c.set_priority(10); @@ -2793,18 +1415,626 @@ HashMap<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_fill_pattern(int p_ } // Fills in the constraint list from modified tiles border. - for (TerrainConstraint c : _get_terrain_constraints_from_painted_cells_list(p_layer, painted_set, p_terrain_set, p_ignore_empty_terrains)) { + for (TerrainConstraint c : _get_terrain_constraints_from_painted_cells_list(painted_set, p_terrain_set, p_ignore_empty_terrains)) { constraints.insert(c); } // Fill the terrains. - output = terrain_fill_constraints(p_layer, can_modify_list, p_terrain_set, constraints); + output = terrain_fill_constraints(can_modify_list, p_terrain_set, constraints); return output; } -void TileMap::set_cells_terrain_connect(int p_layer, TypedArray<Vector2i> p_cells, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains) { +TileMapCell TileMapLayer::get_cell(const Vector2i &p_coords, bool p_use_proxies) const { + if (!tile_map.has(p_coords)) { + return TileMapCell(); + } else { + TileMapCell c = tile_map.find(p_coords)->value; + const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); + if (p_use_proxies && tile_set.is_valid()) { + Array proxyed = tile_set->map_tile_proxy(c.source_id, c.get_atlas_coords(), c.alternative_tile); + c.source_id = proxyed[0]; + c.set_atlas_coords(proxyed[1]); + c.alternative_tile = proxyed[2]; + } + return c; + } +} + +int TileMapLayer::get_effective_quadrant_size() const { + // When using YSort, the quadrant size is reduced to 1 to have one CanvasItem per quadrant. + if (tile_map_node->is_y_sort_enabled() && is_y_sort_enabled()) { + return 1; + } else { + return tile_map_node->get_quadrant_size(); + } +} + +void TileMapLayer::set_tile_data(TileMapLayer::DataFormat p_format, const Vector<int> &p_data) { + ERR_FAIL_COND(p_format > TileMapLayer::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 >= TileMapLayer::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 != TileMapLayer::FORMAT_3, vformat("Cannot handle deprecated TileMap 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 >= TileMapLayer::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 == TileMapLayer::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 == TileMapLayer::FORMAT_2) { + coord_x = decode_uint16(&local[8]); + coord_y = decode_uint16(&local[10]); + } + + const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); + if (tile_set.is_valid()) { + Array a = tile_set->compatibility_tilemap_map(v, Vector2i(coord_x, coord_y), flip_h, flip_v, transpose); + if (a.size() == 3) { + 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<int> TileMapLayer::get_tile_data() const { + // Export tile data to raw format. + Vector<int> tile_data; + tile_data.resize(tile_map.size() * 3); + int *w = tile_data.ptrw(); + + // Save in highest format. + + int idx = 0; + for (const KeyValue<Vector2i, TileMapCell> &E : tile_map) { + 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.source_id, &ptr[4]); + encode_uint16(E.value.coord_x, &ptr[6]); + encode_uint16(E.value.coord_y, &ptr[8]); + encode_uint16(E.value.alternative_tile, &ptr[10]); + idx += 3; + } + + return tile_data; +} + +void TileMapLayer::clear_instantiated_scenes() { + instantiated_scenes.clear(); +} + +void TileMapLayer::clear_internals() { + // Clear quadrants. + while (quadrant_map.size()) { + _erase_quadrant(quadrant_map.begin()); + } + + // Clear the layers internals. + _rendering_cleanup(); + + // Clear the layers internal navigation maps. + _navigation_cleanup(); + + // Clear the dirty quadrants list. + while (dirty_quadrant_list.first()) { + dirty_quadrant_list.remove(dirty_quadrant_list.first()); + } +} + +void TileMapLayer::recreate_internals() { + // Make sure that _clear_internals() was called prior. + ERR_FAIL_COND_MSG(quadrant_map.size() > 0, "TileMap layer had a non-empty quadrant map."); + + if (!enabled) { + return; + } + + // Update the layer internals. + _rendering_update(); + + // Update the layer internal navigation maps. + _navigation_update(); + + // Recreate the quadrants. + for (const KeyValue<Vector2i, TileMapCell> &E : tile_map) { + Vector2i qk = _coords_to_quadrant_coords(Vector2i(E.key.x, E.key.y)); + + HashMap<Vector2i, TileMapQuadrant>::Iterator Q = quadrant_map.find(qk); + if (!Q) { + Q = _create_quadrant(qk); + dirty_quadrant_list.add(&Q->value.dirty_list_element); + } + + Vector2i pk = E.key; + Q->value.cells.insert(pk); + + _make_quadrant_dirty(Q); + } + + tile_map_node->queue_update_dirty_quadrants(); +} + +void TileMapLayer::notify_canvas_entered() { + // Rendering. + bool node_visible = tile_map_node->is_visible_in_tree(); + for (KeyValue<Vector2i, TileMapQuadrant> &E_quadrant : quadrant_map) { + TileMapQuadrant &q = E_quadrant.value; + for (const KeyValue<Vector2i, RID> &kv : q.occluders) { + Transform2D xform; + xform.set_origin(tile_map_node->map_to_local(kv.key)); + RS::get_singleton()->canvas_light_occluder_attach_to_canvas(kv.value, tile_map_node->get_canvas()); + RS::get_singleton()->canvas_light_occluder_set_transform(kv.value, tile_map_node->get_global_transform() * xform); + RS::get_singleton()->canvas_light_occluder_set_enabled(kv.value, node_visible); + } + } +} + +void TileMapLayer::notify_visibility_changed() { + bool node_visible = tile_map_node->is_visible_in_tree(); + for (KeyValue<Vector2i, TileMapQuadrant> &E_quadrant : quadrant_map) { + TileMapQuadrant &q = E_quadrant.value; + + // Update occluders transform. + for (const KeyValue<Vector2, Vector2i> &E_cell : q.local_to_map) { + Transform2D xform; + xform.set_origin(E_cell.key); + for (const KeyValue<Vector2i, RID> &kv : q.occluders) { + RS::get_singleton()->canvas_light_occluder_set_enabled(kv.value, node_visible); + } + } + } +} + +void TileMapLayer::notify_xform_changed() { + if (!tile_map_node->is_inside_tree()) { + return; + } + + bool in_editor = false; +#ifdef TOOLS_ENABLED + in_editor = Engine::get_singleton()->is_editor_hint(); +#endif + + Transform2D tilemap_xform = tile_map_node->get_global_transform(); + for (KeyValue<Vector2i, TileMapQuadrant> &E_quadrant : quadrant_map) { + TileMapQuadrant &q = E_quadrant.value; + + // Update occluders transform. + for (const KeyValue<Vector2i, RID> &kv : q.occluders) { + Transform2D xform; + xform.set_origin(tile_map_node->map_to_local(kv.key)); + RenderingServer::get_singleton()->canvas_light_occluder_set_transform(kv.value, tilemap_xform * xform); + } + + // Update navigation regions transform. + for (const KeyValue<Vector2i, Vector<RID>> &E_region : q.navigation_regions) { + for (const RID ®ion : E_region.value) { + if (!region.is_valid()) { + continue; + } + Transform2D tile_transform; + tile_transform.set_origin(tile_map_node->map_to_local(E_region.key)); + NavigationServer2D::get_singleton()->region_set_transform(region, tilemap_xform * tile_transform); + } + } + + // Physics. + if (!tile_map_node->is_collision_animatable() || in_editor) { + for (RID body : q.bodies) { + Transform2D xform; + xform.set_origin(tile_map_node->map_to_local(bodies_coords[body])); + xform = tilemap_xform * xform; + PhysicsServer2D::get_singleton()->body_set_state(body, PhysicsServer2D::BODY_STATE_TRANSFORM, xform); + } + } + } +} + +void TileMapLayer::notify_local_xform_changed() { + if (!tile_map_node->is_inside_tree()) { + return; + } + + bool in_editor = false; +#ifdef TOOLS_ENABLED + in_editor = Engine::get_singleton()->is_editor_hint(); +#endif + if (!tile_map_node->is_collision_animatable() || in_editor) { + Transform2D gl_transform = tile_map_node->get_global_transform(); + for (KeyValue<Vector2i, TileMapQuadrant> &E : quadrant_map) { + TileMapQuadrant &q = E.value; + + for (RID body : q.bodies) { + Transform2D xform; + xform.set_origin(tile_map_node->map_to_local(bodies_coords[body])); + xform = gl_transform * xform; + PhysicsServer2D::get_singleton()->body_set_state(body, PhysicsServer2D::BODY_STATE_TRANSFORM, xform); + } + } + } +} + +void TileMapLayer::notify_canvas_exited() { + for (KeyValue<Vector2i, TileMapQuadrant> &E_quadrant : quadrant_map) { + TileMapQuadrant &q = E_quadrant.value; + for (const KeyValue<Vector2i, RID> &kv : q.occluders) { + RS::get_singleton()->canvas_light_occluder_attach_to_canvas(kv.value, RID()); + } + } +} + +void TileMapLayer::notify_selected_layer_changed() { + _rendering_update(); +} + +void TileMapLayer::notify_light_mask_changed() { + for (const KeyValue<Vector2i, TileMapQuadrant> &E : quadrant_map) { + for (const RID &ci : E.value.canvas_items) { + RenderingServer::get_singleton()->canvas_item_set_light_mask(ci, tile_map_node->get_light_mask()); + } + } + _rendering_update(); +} + +void TileMapLayer::notify_material_changed() { + for (KeyValue<Vector2i, TileMapQuadrant> &E : quadrant_map) { + TileMapQuadrant &q = E.value; + for (const RID &ci : q.canvas_items) { + RS::get_singleton()->canvas_item_set_use_parent_material(ci, tile_map_node->get_use_parent_material() || tile_map_node->get_material().is_valid()); + } + } + _rendering_update(); +} + +void TileMapLayer::notify_use_parent_material_changed() { + notify_material_changed(); +} + +void TileMapLayer::notify_texture_filter_changed() { + for (HashMap<Vector2i, TileMapQuadrant>::Iterator F = quadrant_map.begin(); F; ++F) { + TileMapQuadrant &q = F->value; + for (const RID &ci : q.canvas_items) { + RenderingServer::get_singleton()->canvas_item_set_default_texture_filter(ci, RS::CanvasItemTextureFilter(tile_map_node->get_texture_filter_in_tree())); + _make_quadrant_dirty(F); + } + } + _rendering_update(); +} + +void TileMapLayer::notify_texture_repeat_changed() { + for (HashMap<Vector2i, TileMapQuadrant>::Iterator F = quadrant_map.begin(); F; ++F) { + TileMapQuadrant &q = F->value; + for (const RID &ci : q.canvas_items) { + RenderingServer::get_singleton()->canvas_item_set_default_texture_repeat(ci, RS::CanvasItemTextureRepeat(tile_map_node->get_texture_repeat_in_tree())); + _make_quadrant_dirty(F); + } + } + _rendering_update(); +} + +void TileMapLayer::update_dirty_quadrants() { + // Update the coords cache. + for (SelfList<TileMapQuadrant> *q = dirty_quadrant_list.first(); q; q = q->next()) { + q->self()->map_to_local.clear(); + q->self()->local_to_map.clear(); + for (const Vector2i &E : q->self()->cells) { + Vector2i pk = E; + Vector2 pk_local_coords = tile_map_node->map_to_local(pk); + q->self()->map_to_local[pk] = pk_local_coords; + q->self()->local_to_map[pk_local_coords] = pk; + } + } + + // Find TileData that need a runtime modification. + _build_runtime_update_tile_data(dirty_quadrant_list); + + // Call the update_dirty_quadrant method on plugins. + _rendering_update_dirty_quadrants(dirty_quadrant_list); + _physics_update_dirty_quadrants(dirty_quadrant_list); + _navigation_update_dirty_quadrants(dirty_quadrant_list); + _scenes_update_dirty_quadrants(dirty_quadrant_list); + + // Redraw the debug canvas_items. + RenderingServer *rs = RenderingServer::get_singleton(); + for (SelfList<TileMapQuadrant> *q = dirty_quadrant_list.first(); q; q = q->next()) { + rs->canvas_item_clear(q->self()->debug_canvas_item); + Transform2D xform; + xform.set_origin(tile_map_node->map_to_local(q->self()->coords * get_effective_quadrant_size())); + rs->canvas_item_set_transform(q->self()->debug_canvas_item, xform); + + _rendering_draw_quadrant_debug(q->self()); + _physics_draw_quadrant_debug(q->self()); + _navigation_draw_quadrant_debug(q->self()); + _scenes_draw_quadrant_debug(q->self()); + } + + // Clear the list. + while (dirty_quadrant_list.first()) { + // Clear the runtime tile data. + for (const KeyValue<Vector2i, TileData *> &kv : dirty_quadrant_list.first()->self()->runtime_tile_data_cache) { + memdelete(kv.value); + } + + dirty_quadrant_list.remove(dirty_quadrant_list.first()); + } +} + +void TileMapLayer::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<Vector2i, TileMapCell>::Iterator E = tile_map.find(pk); + + int source_id = p_source_id; + Vector2i atlas_coords = p_atlas_coords; + int alternative_tile = p_alternative_tile; + + if ((source_id == TileSet::INVALID_SOURCE || atlas_coords == TileSetSource::INVALID_ATLAS_COORDS || alternative_tile == TileSetSource::INVALID_TILE_ALTERNATIVE) && + (source_id != TileSet::INVALID_SOURCE || atlas_coords != TileSetSource::INVALID_ATLAS_COORDS || alternative_tile != TileSetSource::INVALID_TILE_ALTERNATIVE)) { + source_id = TileSet::INVALID_SOURCE; + atlas_coords = TileSetSource::INVALID_ATLAS_COORDS; + alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE; + } + + if (!E && source_id == TileSet::INVALID_SOURCE) { + return; // Nothing to do, the tile is already empty. + } + + // Get the quadrant + Vector2i qk = _coords_to_quadrant_coords(pk); + + HashMap<Vector2i, TileMapQuadrant>::Iterator Q = quadrant_map.find(qk); + + if (source_id == TileSet::INVALID_SOURCE) { + // Erase existing cell in the tile map. + tile_map.erase(pk); + + // Erase existing cell in the quadrant. + ERR_FAIL_COND(!Q); + TileMapQuadrant &q = Q->value; + + q.cells.erase(pk); + + // Remove or make the quadrant dirty. + if (q.cells.size() == 0) { + _erase_quadrant(Q); + } else { + _make_quadrant_dirty(Q); + } + + used_rect_cache_dirty = true; + } else { + if (!E) { + // Insert a new cell in the tile map. + E = tile_map.insert(pk, TileMapCell()); + + // Create a new quadrant if needed, then insert the cell if needed. + if (!Q) { + Q = _create_quadrant(qk); + } + TileMapQuadrant &q = Q->value; + q.cells.insert(pk); + + } else { + ERR_FAIL_COND(!Q); // TileMapQuadrant should exist... + + if (E->value.source_id == source_id && E->value.get_atlas_coords() == atlas_coords && E->value.alternative_tile == alternative_tile) { + return; // Nothing changed. + } + } + + TileMapCell &c = E->value; + + c.source_id = source_id; + c.set_atlas_coords(atlas_coords); + c.alternative_tile = alternative_tile; + + _make_quadrant_dirty(Q); + used_rect_cache_dirty = true; + } +} + +void TileMapLayer::erase_cell(const Vector2i &p_coords) { + set_cell(p_coords, TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE); +} + +int TileMapLayer::get_cell_source_id(const Vector2i &p_coords, bool p_use_proxies) const { + // Get a cell source id from position. + HashMap<Vector2i, TileMapCell>::ConstIterator E = tile_map.find(p_coords); + + if (!E) { + return TileSet::INVALID_SOURCE; + } + + const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); + if (p_use_proxies && tile_set.is_valid()) { + Array proxyed = tile_set->map_tile_proxy(E->value.source_id, E->value.get_atlas_coords(), E->value.alternative_tile); + return proxyed[0]; + } + + return E->value.source_id; +} + +Vector2i TileMapLayer::get_cell_atlas_coords(const Vector2i &p_coords, bool p_use_proxies) const { + // Get a cell source id from position. + HashMap<Vector2i, TileMapCell>::ConstIterator E = tile_map.find(p_coords); + + if (!E) { + return TileSetSource::INVALID_ATLAS_COORDS; + } + + const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); + if (p_use_proxies && tile_set.is_valid()) { + Array proxyed = tile_set->map_tile_proxy(E->value.source_id, E->value.get_atlas_coords(), E->value.alternative_tile); + return proxyed[1]; + } + + return E->value.get_atlas_coords(); +} + +int TileMapLayer::get_cell_alternative_tile(const Vector2i &p_coords, bool p_use_proxies) const { + // Get a cell source id from position. + HashMap<Vector2i, TileMapCell>::ConstIterator E = tile_map.find(p_coords); + + if (!E) { + return TileSetSource::INVALID_TILE_ALTERNATIVE; + } + + const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); + if (p_use_proxies && tile_set.is_valid()) { + Array proxyed = tile_set->map_tile_proxy(E->value.source_id, E->value.get_atlas_coords(), E->value.alternative_tile); + return proxyed[2]; + } + + return E->value.alternative_tile; +} + +TileData *TileMapLayer::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 == TileSet::INVALID_SOURCE) { + return nullptr; + } + + const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); + Ref<TileSetAtlasSource> source = tile_set->get_source(source_id); + if (source.is_valid()) { + return source->get_tile_data(get_cell_atlas_coords(p_coords, p_use_proxies), get_cell_alternative_tile(p_coords, p_use_proxies)); + } + + return nullptr; +} + +void TileMapLayer::clear() { + // Remove all tiles. + clear_internals(); + tile_map.clear(); + recreate_internals(); + used_rect_cache_dirty = true; +} + +Ref<TileMapPattern> TileMapLayer::get_pattern(TypedArray<Vector2i> p_coords_array) { + const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); + ERR_FAIL_COND_V(!tile_set.is_valid(), nullptr); + + Ref<TileMapPattern> output; + output.instantiate(); + if (p_coords_array.is_empty()) { + return output; + } + + Vector2i min = Vector2i(p_coords_array[0]); + for (int i = 1; i < p_coords_array.size(); i++) { + min = min.min(p_coords_array[i]); + } + + Vector<Vector2i> coords_in_pattern_array; + coords_in_pattern_array.resize(p_coords_array.size()); + Vector2i ensure_positive_offset; + for (int i = 0; i < p_coords_array.size(); i++) { + Vector2i coords = p_coords_array[i]; + Vector2i coords_in_pattern = coords - min; + if (tile_set->get_tile_shape() != TileSet::TILE_SHAPE_SQUARE) { + if (tile_set->get_tile_layout() == TileSet::TILE_LAYOUT_STACKED) { + if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_HORIZONTAL && bool(min.y % 2) && bool(coords_in_pattern.y % 2)) { + coords_in_pattern.x -= 1; + if (coords_in_pattern.x < 0) { + ensure_positive_offset.x = 1; + } + } else if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_VERTICAL && bool(min.x % 2) && bool(coords_in_pattern.x % 2)) { + coords_in_pattern.y -= 1; + if (coords_in_pattern.y < 0) { + ensure_positive_offset.y = 1; + } + } + } else if (tile_set->get_tile_layout() == TileSet::TILE_LAYOUT_STACKED_OFFSET) { + if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_HORIZONTAL && bool(min.y % 2) && bool(coords_in_pattern.y % 2)) { + coords_in_pattern.x += 1; + } else if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_VERTICAL && bool(min.x % 2) && bool(coords_in_pattern.x % 2)) { + coords_in_pattern.y += 1; + } + } + } + coords_in_pattern_array.write[i] = coords_in_pattern; + } + + for (int i = 0; i < coords_in_pattern_array.size(); i++) { + Vector2i coords = p_coords_array[i]; + Vector2i coords_in_pattern = coords_in_pattern_array[i]; + output->set_cell(coords_in_pattern + ensure_positive_offset, get_cell_source_id(coords), get_cell_atlas_coords(coords), get_cell_alternative_tile(coords)); + } + + return output; +} + +void TileMapLayer::set_pattern(const Vector2i &p_position, const Ref<TileMapPattern> p_pattern) { + const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); + ERR_FAIL_COND(tile_set.is_null()); + ERR_FAIL_COND(p_pattern.is_null()); + + TypedArray<Vector2i> used_cells = p_pattern->get_used_cells(); + for (int i = 0; i < used_cells.size(); i++) { + Vector2i coords = tile_map_node->map_pattern(p_position, used_cells[i], p_pattern); + set_cell(coords, p_pattern->get_cell_source_id(used_cells[i]), p_pattern->get_cell_atlas_coords(used_cells[i]), p_pattern->get_cell_alternative_tile(used_cells[i])); + } +} + +void TileMapLayer::set_cells_terrain_connect(TypedArray<Vector2i> p_cells, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains) { + const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); ERR_FAIL_COND(!tile_set.is_valid()); - ERR_FAIL_INDEX(p_layer, (int)layers.size()); ERR_FAIL_INDEX(p_terrain_set, tile_set->get_terrain_sets_count()); Vector<Vector2i> cells_vector; @@ -2813,16 +2043,16 @@ void TileMap::set_cells_terrain_connect(int p_layer, TypedArray<Vector2i> p_cell cells_vector.push_back(p_cells[i]); painted_set.insert(p_cells[i]); } - HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_output = terrain_fill_connect(p_layer, cells_vector, p_terrain_set, p_terrain, p_ignore_empty_terrains); + HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_output = terrain_fill_connect(cells_vector, p_terrain_set, p_terrain, p_ignore_empty_terrains); for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &kv : terrain_fill_output) { if (painted_set.has(kv.key)) { // Paint a random tile with the correct terrain for the painted path. TileMapCell c = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, kv.value); - set_cell(p_layer, kv.key, c.source_id, c.get_atlas_coords(), c.alternative_tile); + set_cell(kv.key, c.source_id, c.get_atlas_coords(), c.alternative_tile); } else { // Avoids updating the painted path from the output if the new pattern is the same as before. TileSet::TerrainsPattern in_map_terrain_pattern = TileSet::TerrainsPattern(*tile_set, p_terrain_set); - TileMapCell cell = get_cell(p_layer, kv.key); + TileMapCell cell = get_cell(kv.key); if (cell.source_id != TileSet::INVALID_SOURCE) { TileSetSource *source = *tile_set->get_source(cell.source_id); TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); @@ -2836,15 +2066,15 @@ void TileMap::set_cells_terrain_connect(int p_layer, TypedArray<Vector2i> p_cell } if (in_map_terrain_pattern != kv.value) { TileMapCell c = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, kv.value); - set_cell(p_layer, kv.key, c.source_id, c.get_atlas_coords(), c.alternative_tile); + set_cell(kv.key, c.source_id, c.get_atlas_coords(), c.alternative_tile); } } } } -void TileMap::set_cells_terrain_path(int p_layer, TypedArray<Vector2i> p_path, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains) { +void TileMapLayer::set_cells_terrain_path(TypedArray<Vector2i> p_path, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains) { + const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); ERR_FAIL_COND(!tile_set.is_valid()); - ERR_FAIL_INDEX(p_layer, (int)layers.size()); ERR_FAIL_INDEX(p_terrain_set, tile_set->get_terrain_sets_count()); Vector<Vector2i> vector_path; @@ -2854,16 +2084,16 @@ void TileMap::set_cells_terrain_path(int p_layer, TypedArray<Vector2i> p_path, i painted_set.insert(p_path[i]); } - HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_output = terrain_fill_path(p_layer, vector_path, p_terrain_set, p_terrain, p_ignore_empty_terrains); + HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_output = terrain_fill_path(vector_path, p_terrain_set, p_terrain, p_ignore_empty_terrains); for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &kv : terrain_fill_output) { if (painted_set.has(kv.key)) { // Paint a random tile with the correct terrain for the painted path. TileMapCell c = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, kv.value); - set_cell(p_layer, kv.key, c.source_id, c.get_atlas_coords(), c.alternative_tile); + set_cell(kv.key, c.source_id, c.get_atlas_coords(), c.alternative_tile); } else { // Avoids updating the painted path from the output if the new pattern is the same as before. TileSet::TerrainsPattern in_map_terrain_pattern = TileSet::TerrainsPattern(*tile_set, p_terrain_set); - TileMapCell cell = get_cell(p_layer, kv.key); + TileMapCell cell = get_cell(kv.key); if (cell.source_id != TileSet::INVALID_SOURCE) { TileSetSource *source = *tile_set->get_source(cell.source_id); TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); @@ -2877,246 +2107,1233 @@ void TileMap::set_cells_terrain_path(int p_layer, TypedArray<Vector2i> p_path, i } if (in_map_terrain_pattern != kv.value) { TileMapCell c = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, kv.value); - set_cell(p_layer, kv.key, c.source_id, c.get_atlas_coords(), c.alternative_tile); + set_cell(kv.key, c.source_id, c.get_atlas_coords(), c.alternative_tile); } } } } -TileMapCell TileMap::get_cell(int p_layer, const Vector2i &p_coords, bool p_use_proxies) const { - ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), TileMapCell()); - const HashMap<Vector2i, TileMapCell> &tile_map = layers[p_layer].tile_map; - if (!tile_map.has(p_coords)) { - return TileMapCell(); - } else { - TileMapCell c = tile_map.find(p_coords)->value; - 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; +TypedArray<Vector2i> TileMapLayer::get_used_cells() const { + // Returns the cells used in the tilemap. + TypedArray<Vector2i> a; + a.resize(tile_map.size()); + int i = 0; + for (const KeyValue<Vector2i, TileMapCell> &E : tile_map) { + Vector2i p(E.key.x, E.key.y); + a[i++] = p; } -} - -HashMap<Vector2i, TileMapQuadrant> *TileMap::get_quadrant_map(int p_layer) { - ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), nullptr); - return &layers[p_layer].quadrant_map; + return a; } -Vector2i TileMap::get_coords_for_body_rid(RID p_physics_body) { - ERR_FAIL_COND_V_MSG(!bodies_coords.has(p_physics_body), Vector2i(), vformat("No tiles for the given body RID %d.", p_physics_body)); - return bodies_coords[p_physics_body]; -} +TypedArray<Vector2i> TileMapLayer::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. + TypedArray<Vector2i> a; + for (const KeyValue<Vector2i, TileMapCell> &E : tile_map) { + if ((p_source_id == TileSet::INVALID_SOURCE || p_source_id == E.value.source_id) && + (p_atlas_coords == TileSetSource::INVALID_ATLAS_COORDS || p_atlas_coords == E.value.get_atlas_coords()) && + (p_alternative_tile == TileSetSource::INVALID_TILE_ALTERNATIVE || p_alternative_tile == E.value.alternative_tile)) { + a.push_back(E.key); + } + } -int TileMap::get_layer_for_body_rid(RID p_physics_body) { - ERR_FAIL_COND_V_MSG(!bodies_layers.has(p_physics_body), int(), vformat("No tiles for the given body RID %d.", p_physics_body)); - return bodies_layers[p_physics_body]; + return a; } -void TileMap::fix_invalid_tiles() { - ERR_FAIL_COND_MSG(tile_set.is_null(), "Cannot fix invalid tiles if Tileset is not open."); +Rect2i TileMapLayer::get_used_rect() const { + // Return the rect of the currently used area. + if (used_rect_cache_dirty) { + bool first = true; + used_rect_cache = Rect2i(); - for (unsigned int i = 0; i < layers.size(); i++) { - const HashMap<Vector2i, TileMapCell> &tile_map = layers[i].tile_map; - RBSet<Vector2i> coords; - for (const KeyValue<Vector2i, TileMapCell> &E : tile_map) { - TileSetSource *source = *tile_set->get_source(E.value.source_id); - if (!source || !source->has_tile(E.value.get_atlas_coords()) || !source->has_alternative_tile(E.value.get_atlas_coords(), E.value.alternative_tile)) { - coords.insert(E.key); + if (tile_map.size() > 0) { + if (first) { + used_rect_cache = Rect2i(tile_map.begin()->key.x, tile_map.begin()->key.y, 0, 0); + first = false; + } + + for (const KeyValue<Vector2i, TileMapCell> &E : tile_map) { + used_rect_cache.expand_to(Vector2i(E.key.x, E.key.y)); } } - for (const Vector2i &E : coords) { - set_cell(i, E, TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE); + + if (!first) { // first is true if every layer is empty. + used_rect_cache.size += Vector2i(1, 1); // The cache expands to top-left coordinate, so we add one full tile. } + used_rect_cache_dirty = false; } + + return used_rect_cache; } -void TileMap::clear_layer(int p_layer) { - ERR_FAIL_INDEX(p_layer, (int)layers.size()); +void TileMapLayer::set_name(String p_name) { + if (name == p_name) { + return; + } + name = p_name; + tile_map_node->emit_signal(CoreStringNames::get_singleton()->changed); +} - // Remove all tiles. - _clear_layer_internals(p_layer); - layers[p_layer].tile_map.clear(); - _recreate_layer_internals(p_layer); - used_rect_cache_dirty = true; +String TileMapLayer::get_name() const { + return name; } -void TileMap::clear() { - // Remove all tiles. - _clear_internals(); - for (TileMapLayer &layer : layers) { - layer.tile_map.clear(); +void TileMapLayer::set_enabled(bool p_enabled) { + if (enabled == p_enabled) { + return; } - _recreate_internals(); - used_rect_cache_dirty = true; + enabled = p_enabled; + clear_internals(); + recreate_internals(); + tile_map_node->emit_signal(CoreStringNames::get_singleton()->changed); + + tile_map_node->update_configuration_warnings(); } -void TileMap::force_update(int p_layer) { - if (p_layer >= 0) { - ERR_FAIL_INDEX(p_layer, (int)layers.size()); - _clear_layer_internals(p_layer); - _recreate_layer_internals(p_layer); - } else { - _clear_internals(); - _recreate_internals(); +bool TileMapLayer::is_enabled() const { + return enabled; +} + +void TileMapLayer::set_modulate(Color p_modulate) { + if (modulate == p_modulate) { + return; } + modulate = p_modulate; + _rendering_update(); + tile_map_node->emit_signal(CoreStringNames::get_singleton()->changed); } -void TileMap::_set_tile_data(int p_layer, const Vector<int> &p_data) { - ERR_FAIL_INDEX(p_layer, (int)layers.size()); - ERR_FAIL_COND(format > FORMAT_3); +Color TileMapLayer::get_modulate() const { + return modulate; +} - // Set data for a given tile from raw data. +void TileMapLayer::set_y_sort_enabled(bool p_y_sort_enabled) { + if (y_sort_enabled == p_y_sort_enabled) { + return; + } + y_sort_enabled = p_y_sort_enabled; + clear_internals(); + recreate_internals(); + tile_map_node->emit_signal(CoreStringNames::get_singleton()->changed); - int c = p_data.size(); - const int *r = p_data.ptr(); + tile_map_node->update_configuration_warnings(); +} - int offset = (format >= FORMAT_2) ? 3 : 2; - ERR_FAIL_COND_MSG(c % offset != 0, vformat("Corrupted tile data. Got size: %s. Expected modulo: %s", offset)); +bool TileMapLayer::is_y_sort_enabled() const { + return y_sort_enabled; +} - clear_layer(p_layer); +void TileMapLayer::set_y_sort_origin(int p_y_sort_origin) { + if (y_sort_origin == p_y_sort_origin) { + return; + } + y_sort_origin = p_y_sort_origin; + clear_internals(); + recreate_internals(); + tile_map_node->emit_signal(CoreStringNames::get_singleton()->changed); +} -#ifdef DISABLE_DEPRECATED - ERR_FAIL_COND_MSG(format != FORMAT_3, vformat("Cannot handle deprecated TileMap data format version %d. This Godot version was compiled with no support for deprecated data.", format)); -#endif +int TileMapLayer::get_y_sort_origin() const { + return y_sort_origin; +} - for (int i = 0; i < c; i += offset) { - const uint8_t *ptr = (const uint8_t *)&r[i]; - uint8_t local[12]; - for (int j = 0; j < ((format >= FORMAT_2) ? 12 : 8); j++) { - local[j] = ptr[j]; +void TileMapLayer::set_z_index(int p_z_index) { + if (z_index == p_z_index) { + return; + } + z_index = p_z_index; + _rendering_update(); + tile_map_node->emit_signal(CoreStringNames::get_singleton()->changed); + + tile_map_node->update_configuration_warnings(); +} + +int TileMapLayer::get_z_index() const { + return z_index; +} + +void TileMapLayer::set_navigation_map(RID p_map) { + ERR_FAIL_COND_MSG(!tile_map_node->is_inside_tree(), "A TileMap navigation map can only be changed while inside the SceneTree."); + navigation_map = p_map; + uses_world_navigation_map = p_map == tile_map_node->get_world_2d()->get_navigation_map(); +} + +RID TileMapLayer::get_navigation_map() const { + if (navigation_map.is_valid()) { + return navigation_map; + } + return RID(); +} + +void TileMapLayer::force_update() { + clear_internals(); + recreate_internals(); +} + +void TileMapLayer::fix_invalid_tiles() { + RBSet<Vector2i> coords; + for (const KeyValue<Vector2i, TileMapCell> &E : tile_map) { + TileSetSource *source = *(tile_map_node->get_tileset())->get_source(E.value.source_id); + if (!source || !source->has_tile(E.value.get_atlas_coords()) || !source->has_alternative_tile(E.value.get_atlas_coords(), E.value.alternative_tile)) { + coords.insert(E.key); } + } + for (const Vector2i &E : coords) { + set_cell(E, TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE); + } +} -#ifdef BIG_ENDIAN_ENABLED +bool TileMapLayer::has_body_rid(RID p_physics_body) const { + return bodies_coords.has(p_physics_body); +} - 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]); +Vector2i TileMapLayer::get_coords_for_body_rid(RID p_physics_body) const { + return bodies_coords[p_physics_body]; +} + +HashMap<Vector2i, TileSet::CellNeighbor> TerrainConstraint::get_overlapping_coords_and_peering_bits() const { + HashMap<Vector2i, TileSet::CellNeighbor> output; + + ERR_FAIL_COND_V(is_center_bit(), output); + + Ref<TileSet> ts = tile_map->get_tileset(); + ERR_FAIL_COND_V(!ts.is_valid(), output); + + TileSet::TileShape shape = ts->get_tile_shape(); + if (shape == TileSet::TILE_SHAPE_SQUARE) { + switch (bit) { + case 1: + output[base_cell_coords] = TileSet::CELL_NEIGHBOR_RIGHT_SIDE; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_LEFT_SIDE; + break; + case 2: + output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER; + break; + case 3: + output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_SIDE; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_SIDE; + break; + default: + ERR_FAIL_V(output); } -#endif - // Extracts position in TileMap. - int16_t x = decode_uint16(&local[0]); - int16_t y = decode_uint16(&local[2]); + } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC) { + switch (bit) { + case 1: + output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE; + break; + case 2: + output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_CORNER; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_LEFT_CORNER; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_CORNER)] = TileSet::CELL_NEIGHBOR_TOP_CORNER; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_RIGHT_CORNER; + break; + case 3: + output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE; + break; + default: + ERR_FAIL_V(output); + } + } else { + // Half offset shapes. + TileSet::TileOffsetAxis offset_axis = ts->get_tile_offset_axis(); + if (offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { + switch (bit) { + case 1: + output[base_cell_coords] = TileSet::CELL_NEIGHBOR_RIGHT_SIDE; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_LEFT_SIDE; + break; + case 2: + output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_CORNER; + break; + case 3: + output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE; + break; + case 4: + output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_CORNER; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER; + break; + case 5: + output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE; + break; + default: + ERR_FAIL_V(output); + } + } else { + switch (bit) { + case 1: + output[base_cell_coords] = TileSet::CELL_NEIGHBOR_RIGHT_CORNER; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER; + break; + case 2: + output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE; + break; + case 3: + output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_LEFT_CORNER; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER; + break; + case 4: + output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_SIDE; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_SIDE; + break; + case 5: + output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE; + break; + default: + ERR_FAIL_V(output); + } + } + } + return output; +} - if (format == FORMAT_3) { - uint16_t source_id = decode_uint16(&local[4]); - uint16_t atlas_coords_x = decode_uint16(&local[6]); - uint16_t atlas_coords_y = decode_uint16(&local[8]); - uint16_t alternative_tile = decode_uint16(&local[10]); - set_cell(p_layer, Vector2i(x, y), source_id, Vector2i(atlas_coords_x, atlas_coords_y), alternative_tile); +TerrainConstraint::TerrainConstraint(const TileMap *p_tile_map, const Vector2i &p_position, int p_terrain) { + tile_map = p_tile_map; + + Ref<TileSet> ts = tile_map->get_tileset(); + ERR_FAIL_COND(!ts.is_valid()); + + bit = 0; + base_cell_coords = p_position; + terrain = p_terrain; +} + +TerrainConstraint::TerrainConstraint(const TileMap *p_tile_map, const Vector2i &p_position, const TileSet::CellNeighbor &p_bit, int p_terrain) { + // The way we build the constraint make it easy to detect conflicting constraints. + tile_map = p_tile_map; + + Ref<TileSet> ts = tile_map->get_tileset(); + ERR_FAIL_COND(!ts.is_valid()); + + TileSet::TileShape shape = ts->get_tile_shape(); + if (shape == TileSet::TILE_SHAPE_SQUARE) { + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_RIGHT_SIDE: + bit = 1; + base_cell_coords = p_position; + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: + bit = 2; + base_cell_coords = p_position; + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE: + bit = 3; + base_cell_coords = p_position; + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER: + bit = 2; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_LEFT_SIDE); + break; + case TileSet::CELL_NEIGHBOR_LEFT_SIDE: + bit = 1; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_LEFT_SIDE); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER: + bit = 2; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER); + break; + case TileSet::CELL_NEIGHBOR_TOP_SIDE: + bit = 3; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_SIDE); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER: + bit = 2; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_SIDE); + break; + default: + ERR_FAIL(); + break; + } + } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC) { + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_RIGHT_CORNER: + bit = 2; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE: + bit = 1; + base_cell_coords = p_position; + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER: + bit = 2; + base_cell_coords = p_position; + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE: + bit = 3; + base_cell_coords = p_position; + break; + case TileSet::CELL_NEIGHBOR_LEFT_CORNER: + bit = 2; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE: + bit = 1; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE); + break; + case TileSet::CELL_NEIGHBOR_TOP_CORNER: + bit = 2; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_CORNER); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE: + bit = 3; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE); + break; + default: + ERR_FAIL(); + break; + } + } else { + // Half-offset shapes. + TileSet::TileOffsetAxis offset_axis = ts->get_tile_offset_axis(); + if (offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_RIGHT_SIDE: + bit = 1; + base_cell_coords = p_position; + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: + bit = 2; + base_cell_coords = p_position; + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE: + bit = 3; + base_cell_coords = p_position; + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER: + bit = 4; + base_cell_coords = p_position; + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE: + bit = 5; + base_cell_coords = p_position; + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER: + bit = 2; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_LEFT_SIDE); + break; + case TileSet::CELL_NEIGHBOR_LEFT_SIDE: + bit = 1; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_LEFT_SIDE); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER: + bit = 4; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE: + bit = 3; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE); + break; + case TileSet::CELL_NEIGHBOR_TOP_CORNER: + bit = 2; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE: + bit = 5; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER: + bit = 4; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE); + break; + default: + ERR_FAIL(); + break; + } } else { -#ifndef DISABLE_DEPRECATED - // Previous decated format. + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_RIGHT_CORNER: + bit = 1; + base_cell_coords = p_position; + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE: + bit = 2; + base_cell_coords = p_position; + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: + bit = 3; + base_cell_coords = p_position; + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE: + bit = 4; + base_cell_coords = p_position; + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER: + bit = 1; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE: + bit = 5; + base_cell_coords = p_position; + break; + case TileSet::CELL_NEIGHBOR_LEFT_CORNER: + bit = 3; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE: + bit = 2; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER: + bit = 1; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE); + break; + case TileSet::CELL_NEIGHBOR_TOP_SIDE: + bit = 4; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_SIDE); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER: + bit = 3; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_SIDE); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE: + bit = 5; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE); + break; + default: + ERR_FAIL(); + break; + } + } + } + terrain = p_terrain; +} - 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; +#define TILEMAP_CALL_FOR_LAYER(layer, function, ...) \ + if (layer < 0) { \ + layer = layers.size() + layer; \ + }; \ + ERR_FAIL_INDEX(layer, (int)layers.size()); \ + layers[layer]->function(__VA_ARGS__); - // Extract autotile/atlas coords. - int16_t coord_x = 0; - int16_t coord_y = 0; - if (format == FORMAT_2) { - coord_x = decode_uint16(&local[8]); - coord_y = decode_uint16(&local[10]); +#define TILEMAP_CALL_FOR_LAYER_V(layer, err_value, function, ...) \ + if (layer < 0) { \ + layer = layers.size() + layer; \ + }; \ + ERR_FAIL_INDEX_V(layer, (int)layers.size(), err_value); \ + return layers[layer]->function(__VA_ARGS__); + +Vector2i TileMap::transform_coords_layout(const Vector2i &p_coords, TileSet::TileOffsetAxis p_offset_axis, TileSet::TileLayout p_from_layout, TileSet::TileLayout p_to_layout) { + // Transform to stacked layout. + Vector2i output = p_coords; + if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL) { + SWAP(output.x, output.y); + } + switch (p_from_layout) { + case TileSet::TILE_LAYOUT_STACKED: + break; + case TileSet::TILE_LAYOUT_STACKED_OFFSET: + if (output.y % 2) { + output.x -= 1; + } + break; + case TileSet::TILE_LAYOUT_STAIRS_RIGHT: + case TileSet::TILE_LAYOUT_STAIRS_DOWN: + if ((p_from_layout == TileSet::TILE_LAYOUT_STAIRS_RIGHT) ^ (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL)) { + if (output.y < 0 && bool(output.y % 2)) { + output = Vector2i(output.x + output.y / 2 - 1, output.y); + } else { + output = Vector2i(output.x + output.y / 2, output.y); + } + } else { + if (output.x < 0 && bool(output.x % 2)) { + output = Vector2i(output.x / 2 - 1, output.x + output.y * 2); + } else { + output = Vector2i(output.x / 2, output.x + output.y * 2); + } + } + break; + case TileSet::TILE_LAYOUT_DIAMOND_RIGHT: + case TileSet::TILE_LAYOUT_DIAMOND_DOWN: + if ((p_from_layout == TileSet::TILE_LAYOUT_DIAMOND_RIGHT) ^ (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL)) { + if ((output.x + output.y) < 0 && (output.x - output.y) % 2) { + output = Vector2i((output.x + output.y) / 2 - 1, output.y - output.x); + } else { + output = Vector2i((output.x + output.y) / 2, -output.x + output.y); + } + } else { + if ((output.x - output.y) < 0 && (output.x + output.y) % 2) { + output = Vector2i((output.x - output.y) / 2 - 1, output.x + output.y); + } else { + output = Vector2i((output.x - output.y) / 2, output.x + output.y); + } } + break; + } - 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(p_layer, Vector2i(x, y), a[0], a[1], a[2]); + switch (p_to_layout) { + case TileSet::TILE_LAYOUT_STACKED: + break; + case TileSet::TILE_LAYOUT_STACKED_OFFSET: + if (output.y % 2) { + output.x += 1; + } + break; + case TileSet::TILE_LAYOUT_STAIRS_RIGHT: + case TileSet::TILE_LAYOUT_STAIRS_DOWN: + if ((p_to_layout == TileSet::TILE_LAYOUT_STAIRS_RIGHT) ^ (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL)) { + if (output.y < 0 && (output.y % 2)) { + output = Vector2i(output.x - output.y / 2 + 1, output.y); } else { - 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)); + output = Vector2i(output.x - output.y / 2, output.y); } } else { - int compatibility_alternative_tile = ((int)flip_h) + ((int)flip_v << 1) + ((int)transpose << 2); - set_cell(p_layer, Vector2i(x, y), v, Vector2i(coord_x, coord_y), compatibility_alternative_tile); + if (output.y % 2) { + if (output.y < 0) { + output = Vector2i(2 * output.x + 1, -output.x + output.y / 2 - 1); + } else { + output = Vector2i(2 * output.x + 1, -output.x + output.y / 2); + } + } else { + output = Vector2i(2 * output.x, -output.x + output.y / 2); + } } + break; + case TileSet::TILE_LAYOUT_DIAMOND_RIGHT: + case TileSet::TILE_LAYOUT_DIAMOND_DOWN: + if ((p_to_layout == TileSet::TILE_LAYOUT_DIAMOND_RIGHT) ^ (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL)) { + if (output.y % 2) { + if (output.y > 0) { + output = Vector2i(output.x - output.y / 2, output.x + output.y / 2 + 1); + } else { + output = Vector2i(output.x - output.y / 2 + 1, output.x + output.y / 2); + } + } else { + output = Vector2i(output.x - output.y / 2, output.x + output.y / 2); + } + } else { + if (output.y % 2) { + if (output.y < 0) { + output = Vector2i(output.x + output.y / 2, -output.x + output.y / 2 - 1); + } else { + output = Vector2i(output.x + output.y / 2 + 1, -output.x + output.y / 2); + } + } else { + output = Vector2i(output.x + output.y / 2, -output.x + output.y / 2); + } + } + break; + } + + if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL) { + SWAP(output.x, output.y); + } + + return output; +} + +void TileMap::set_selected_layer(int p_layer_id) { + ERR_FAIL_COND(p_layer_id < -1 || p_layer_id >= (int)layers.size()); + selected_layer = p_layer_id; + emit_signal(CoreStringNames::get_singleton()->changed); + + // Update the layers modulation. + for (Ref<TileMapLayer> &layer : layers) { + layer->notify_selected_layer_changed(); + } +} + +int TileMap::get_selected_layer() const { + return selected_layer; +} + +void TileMap::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + _clear_internals(); + _recreate_internals(); + } break; + + case NOTIFICATION_EXIT_TREE: { + _clear_internals(); + } break; + } + + // Transfers the notification to tileset plugins. + if (tile_set.is_valid()) { + switch (p_what) { + case TileMap::NOTIFICATION_ENTER_CANVAS: { + for (Ref<TileMapLayer> &layer : layers) { + layer->notify_canvas_entered(); + } + } break; + + case TileMap::NOTIFICATION_EXIT_CANVAS: { + for (Ref<TileMapLayer> &layer : layers) { + layer->notify_canvas_exited(); + } + } break; + + case NOTIFICATION_DRAW: { + // Rendering. + if (tile_set.is_valid()) { + RenderingServer::get_singleton()->canvas_item_set_sort_children_by_y(get_canvas_item(), is_y_sort_enabled()); + } + } break; + + case TileMap::NOTIFICATION_VISIBILITY_CHANGED: { + for (Ref<TileMapLayer> &layer : layers) { + layer->notify_visibility_changed(); + } + } break; + + case NOTIFICATION_TRANSFORM_CHANGED: { + // Physics. + for (Ref<TileMapLayer> &layer : layers) { + layer->notify_xform_changed(); + } + } break; + + case TileMap::NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { + // Physics. + bool in_editor = false; +#ifdef TOOLS_ENABLED + in_editor = Engine::get_singleton()->is_editor_hint(); #endif + if (is_inside_tree() && collision_animatable && !in_editor) { + // Update transform on the physics tick when in animatable mode. + last_valid_transform = new_transform; + set_notify_local_transform(false); + set_global_transform(new_transform); + set_notify_local_transform(true); + } + } break; + + case TileMap::NOTIFICATION_LOCAL_TRANSFORM_CHANGED: { + for (Ref<TileMapLayer> &layer : layers) { + layer->notify_local_xform_changed(); + } + + // Physics. + bool in_editor = false; +#ifdef TOOLS_ENABLED + in_editor = Engine::get_singleton()->is_editor_hint(); +#endif + + // Only active when animatable. Send the new transform to the physics... + if (is_inside_tree() && !in_editor && collision_animatable) { + // ... but then revert changes. + set_notify_local_transform(false); + set_global_transform(last_valid_transform); + set_notify_local_transform(true); + } + } break; } } - emit_signal(SNAME("changed")); } -Vector<int> TileMap::_get_tile_data(int p_layer) const { - ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), Vector<int>()); +void TileMap::queue_update_dirty_quadrants() { + if (pending_update || !is_inside_tree()) { + return; + } + pending_update = true; + call_deferred(SNAME("_update_dirty_quadrants")); +} - // Export tile data to raw format - const HashMap<Vector2i, TileMapCell> &tile_map = layers[p_layer].tile_map; - Vector<int> tile_data; - tile_data.resize(tile_map.size() * 3); - int *w = tile_data.ptrw(); +void TileMap::_update_dirty_quadrants() { + if (!pending_update) { + return; + } - // Save in highest format + if (!is_inside_tree() || !tile_set.is_valid()) { + pending_update = false; + return; + } - int idx = 0; - for (const KeyValue<Vector2i, TileMapCell> &E : tile_map) { - 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.source_id, &ptr[4]); - encode_uint16(E.value.coord_x, &ptr[6]); - encode_uint16(E.value.coord_y, &ptr[8]); - encode_uint16(E.value.alternative_tile, &ptr[10]); - idx += 3; + // Physics: + Transform2D gl_transform = get_global_transform(); + last_valid_transform = gl_transform; + new_transform = gl_transform; + + // Update dirty quadrants on layers. + for (Ref<TileMapLayer> &layer : layers) { + layer->update_dirty_quadrants(); } - return tile_data; + pending_update = false; +} + +void TileMap::set_tileset(const Ref<TileSet> &p_tileset) { + if (p_tileset == tile_set) { + return; + } + + // Set the tileset, registering to its changes. + if (tile_set.is_valid()) { + tile_set->disconnect_changed(callable_mp(this, &TileMap::_tile_set_changed)); + } + + if (!p_tileset.is_valid()) { + _clear_internals(); + } + + tile_set = p_tileset; + + if (tile_set.is_valid()) { + tile_set->connect_changed(callable_mp(this, &TileMap::_tile_set_changed)); + _clear_internals(); + _recreate_internals(); + } + + emit_signal(CoreStringNames::get_singleton()->changed); } -void TileMap::_build_runtime_update_tile_data(SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) { - if (GDVIRTUAL_IS_OVERRIDDEN(_use_tile_data_runtime_update) && GDVIRTUAL_IS_OVERRIDDEN(_tile_data_runtime_update)) { - SelfList<TileMapQuadrant> *q_list_element = r_dirty_quadrant_list.first(); - while (q_list_element) { - TileMapQuadrant &q = *q_list_element->self(); - // Iterate over the cells of the quadrant. - for (const KeyValue<Vector2, Vector2i> &E_cell : q.local_to_map) { - TileMapCell c = get_cell(q.layer, E_cell.value, true); +Ref<TileSet> TileMap::get_tileset() const { + return tile_set; +} - TileSetSource *source; - if (tile_set->has_source(c.source_id)) { - source = *tile_set->get_source(c.source_id); +void TileMap::set_quadrant_size(int p_size) { + ERR_FAIL_COND_MSG(p_size < 1, "TileMapQuadrant size cannot be smaller than 1."); - if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) { - continue; - } + quadrant_size = p_size; + _clear_internals(); + _recreate_internals(); + emit_signal(CoreStringNames::get_singleton()->changed); +} - TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); - if (atlas_source) { - bool ret = false; - if (GDVIRTUAL_CALL(_use_tile_data_runtime_update, q.layer, E_cell.value, ret) && ret) { - TileData *tile_data = atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile); +int TileMap::get_quadrant_size() const { + return quadrant_size; +} - // Create the runtime TileData. - TileData *tile_data_runtime_use = tile_data->duplicate(); - tile_data_runtime_use->set_allow_transform(true); - q.runtime_tile_data_cache[E_cell.value] = tile_data_runtime_use; +void TileMap::draw_tile(RID p_canvas_item, const Vector2 &p_position, const Ref<TileSet> p_tile_set, int p_atlas_source_id, const Vector2i &p_atlas_coords, int p_alternative_tile, int p_frame, Color p_modulation, const TileData *p_tile_data_override, real_t p_animation_offset) { + ERR_FAIL_COND(!p_tile_set.is_valid()); + ERR_FAIL_COND(!p_tile_set->has_source(p_atlas_source_id)); + ERR_FAIL_COND(!p_tile_set->get_source(p_atlas_source_id)->has_tile(p_atlas_coords)); + ERR_FAIL_COND(!p_tile_set->get_source(p_atlas_source_id)->has_alternative_tile(p_atlas_coords, p_alternative_tile)); + TileSetSource *source = *p_tile_set->get_source(p_atlas_source_id); + TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); + if (atlas_source) { + // Check for the frame. + if (p_frame >= 0) { + ERR_FAIL_INDEX(p_frame, atlas_source->get_tile_animation_frames_count(p_atlas_coords)); + } - GDVIRTUAL_CALL(_tile_data_runtime_update, q.layer, E_cell.value, tile_data_runtime_use); - } - } - } + // Get the texture. + Ref<Texture2D> tex = atlas_source->get_runtime_texture(); + if (!tex.is_valid()) { + return; + } + + // Check if we are in the texture, return otherwise. + Vector2i grid_size = atlas_source->get_atlas_grid_size(); + if (p_atlas_coords.x >= grid_size.x || p_atlas_coords.y >= grid_size.y) { + return; + } + + // Get tile data. + const TileData *tile_data = p_tile_data_override ? p_tile_data_override : atlas_source->get_tile_data(p_atlas_coords, p_alternative_tile); + + // Get the tile modulation. + Color modulate = tile_data->get_modulate() * p_modulation; + + // Compute the offset. + Vector2 tile_offset = tile_data->get_texture_origin(); + + // Get destination rect. + Rect2 dest_rect; + dest_rect.size = atlas_source->get_runtime_tile_texture_region(p_atlas_coords).size; + dest_rect.size.x += FP_ADJUST; + dest_rect.size.y += FP_ADJUST; + + bool transpose = tile_data->get_transpose(); + if (transpose) { + dest_rect.position = (p_position - Vector2(dest_rect.size.y, dest_rect.size.x) / 2 - tile_offset); + } else { + dest_rect.position = (p_position - dest_rect.size / 2 - tile_offset); + } + + if (tile_data->get_flip_h()) { + dest_rect.size.x = -dest_rect.size.x; + } + + if (tile_data->get_flip_v()) { + dest_rect.size.y = -dest_rect.size.y; + } + + // Draw the tile. + if (p_frame >= 0) { + Rect2i source_rect = atlas_source->get_runtime_tile_texture_region(p_atlas_coords, p_frame); + tex->draw_rect_region(p_canvas_item, dest_rect, source_rect, modulate, transpose, p_tile_set->is_uv_clipping()); + } else if (atlas_source->get_tile_animation_frames_count(p_atlas_coords) == 1) { + Rect2i source_rect = atlas_source->get_runtime_tile_texture_region(p_atlas_coords, 0); + tex->draw_rect_region(p_canvas_item, dest_rect, source_rect, modulate, transpose, p_tile_set->is_uv_clipping()); + } else { + real_t speed = atlas_source->get_tile_animation_speed(p_atlas_coords); + real_t animation_duration = atlas_source->get_tile_animation_total_duration(p_atlas_coords) / speed; + real_t time = 0.0; + for (int frame = 0; frame < atlas_source->get_tile_animation_frames_count(p_atlas_coords); frame++) { + real_t frame_duration = atlas_source->get_tile_animation_frame_duration(p_atlas_coords, frame) / speed; + RenderingServer::get_singleton()->canvas_item_add_animation_slice(p_canvas_item, animation_duration, time, time + frame_duration, p_animation_offset); + + Rect2i source_rect = atlas_source->get_runtime_tile_texture_region(p_atlas_coords, frame); + tex->draw_rect_region(p_canvas_item, dest_rect, source_rect, modulate, transpose, p_tile_set->is_uv_clipping()); + + time += frame_duration; + } + RenderingServer::get_singleton()->canvas_item_add_animation_slice(p_canvas_item, 1.0, 0.0, 1.0, 0.0); + } + } +} + +int TileMap::get_layers_count() const { + return layers.size(); +} + +void TileMap::add_layer(int p_to_pos) { + if (p_to_pos < 0) { + p_to_pos = layers.size() + p_to_pos + 1; + } + + ERR_FAIL_INDEX(p_to_pos, (int)layers.size() + 1); + + // Must clear before adding the layer. + _clear_internals(); + Ref<TileMapLayer> new_layer; + new_layer.instantiate(); + new_layer->set_tile_map(this); + layers.insert(p_to_pos, new_layer); + for (unsigned int i = 0; i < layers.size(); i++) { + layers[i]->set_layer_index_in_tile_map_node(i); + } + _recreate_internals(); + notify_property_list_changed(); + + emit_signal(CoreStringNames::get_singleton()->changed); + + update_configuration_warnings(); +} + +void TileMap::move_layer(int p_layer, int p_to_pos) { + ERR_FAIL_INDEX(p_layer, (int)layers.size()); + ERR_FAIL_INDEX(p_to_pos, (int)layers.size() + 1); + + // Clear before shuffling layers. + _clear_internals(); + Ref<TileMapLayer> layer = layers[p_layer]; + layers.insert(p_to_pos, layer); + layers.remove_at(p_to_pos < p_layer ? p_layer + 1 : p_layer); + for (unsigned int i = 0; i < layers.size(); i++) { + layers[i]->set_layer_index_in_tile_map_node(i); + } + _recreate_internals(); + notify_property_list_changed(); + + if (selected_layer == p_layer) { + selected_layer = p_to_pos < p_layer ? p_to_pos - 1 : p_to_pos; + } + + emit_signal(CoreStringNames::get_singleton()->changed); + + update_configuration_warnings(); +} + +void TileMap::remove_layer(int p_layer) { + ERR_FAIL_INDEX(p_layer, (int)layers.size()); + + // Clear before removing the layer. + _clear_internals(); + layers.remove_at(p_layer); + for (unsigned int i = 0; i < layers.size(); i++) { + layers[i]->set_layer_index_in_tile_map_node(i); + } + _recreate_internals(); + notify_property_list_changed(); + + if (selected_layer >= p_layer) { + selected_layer -= 1; + } + + emit_signal(CoreStringNames::get_singleton()->changed); + + update_configuration_warnings(); +} + +void TileMap::set_layer_name(int p_layer, String p_name) { + TILEMAP_CALL_FOR_LAYER(p_layer, set_name, p_name); +} + +String TileMap::get_layer_name(int p_layer) const { + TILEMAP_CALL_FOR_LAYER_V(p_layer, "", get_name); +} + +void TileMap::set_layer_enabled(int p_layer, bool p_enabled) { + TILEMAP_CALL_FOR_LAYER(p_layer, set_enabled, p_enabled); +} + +bool TileMap::is_layer_enabled(int p_layer) const { + TILEMAP_CALL_FOR_LAYER_V(p_layer, false, is_enabled); +} + +void TileMap::set_layer_modulate(int p_layer, Color p_modulate) { + TILEMAP_CALL_FOR_LAYER(p_layer, set_modulate, p_modulate); +} + +Color TileMap::get_layer_modulate(int p_layer) const { + TILEMAP_CALL_FOR_LAYER_V(p_layer, Color(), get_modulate); +} + +void TileMap::set_layer_y_sort_enabled(int p_layer, bool p_y_sort_enabled) { + TILEMAP_CALL_FOR_LAYER(p_layer, set_y_sort_enabled, p_y_sort_enabled); +} + +bool TileMap::is_layer_y_sort_enabled(int p_layer) const { + TILEMAP_CALL_FOR_LAYER_V(p_layer, false, is_y_sort_enabled); +} + +void TileMap::set_layer_y_sort_origin(int p_layer, int p_y_sort_origin) { + TILEMAP_CALL_FOR_LAYER(p_layer, set_y_sort_origin, p_y_sort_origin); +} + +int TileMap::get_layer_y_sort_origin(int p_layer) const { + TILEMAP_CALL_FOR_LAYER_V(p_layer, 0, get_y_sort_origin); +} + +void TileMap::set_layer_z_index(int p_layer, int p_z_index) { + TILEMAP_CALL_FOR_LAYER(p_layer, set_z_index, p_z_index); +} + +int TileMap::get_layer_z_index(int p_layer) const { + TILEMAP_CALL_FOR_LAYER_V(p_layer, 0, get_z_index); +} + +void TileMap::set_layer_navigation_map(int p_layer, RID p_map) { + TILEMAP_CALL_FOR_LAYER(p_layer, set_navigation_map, p_map); +} + +RID TileMap::get_layer_navigation_map(int p_layer) const { + TILEMAP_CALL_FOR_LAYER_V(p_layer, RID(), get_navigation_map); +} + +void TileMap::set_collision_animatable(bool p_enabled) { + if (collision_animatable == p_enabled) { + return; + } + collision_animatable = p_enabled; + _clear_internals(); + set_notify_local_transform(p_enabled); + set_physics_process_internal(p_enabled); + _recreate_internals(); + emit_signal(CoreStringNames::get_singleton()->changed); +} + +bool TileMap::is_collision_animatable() const { + return collision_animatable; +} + +void TileMap::set_collision_visibility_mode(TileMap::VisibilityMode p_show_collision) { + if (collision_visibility_mode == p_show_collision) { + return; + } + collision_visibility_mode = p_show_collision; + _clear_internals(); + _recreate_internals(); + emit_signal(CoreStringNames::get_singleton()->changed); +} + +TileMap::VisibilityMode TileMap::get_collision_visibility_mode() { + return collision_visibility_mode; +} + +void TileMap::set_navigation_visibility_mode(TileMap::VisibilityMode p_show_navigation) { + if (navigation_visibility_mode == p_show_navigation) { + return; + } + navigation_visibility_mode = p_show_navigation; + _clear_internals(); + _recreate_internals(); + emit_signal(CoreStringNames::get_singleton()->changed); +} + +TileMap::VisibilityMode TileMap::get_navigation_visibility_mode() { + return navigation_visibility_mode; +} + +void TileMap::set_y_sort_enabled(bool p_enable) { + if (is_y_sort_enabled() == p_enable) { + return; + } + Node2D::set_y_sort_enabled(p_enable); + _clear_internals(); + _recreate_internals(); + emit_signal(CoreStringNames::get_singleton()->changed); + update_configuration_warnings(); +} + +void TileMap::_clear_internals() { + // Clear quadrants. + for (Ref<TileMapLayer> &layer : layers) { + layer->clear_internals(); + } +} + +void TileMap::_recreate_internals() { + for (Ref<TileMapLayer> &layer : layers) { + layer->recreate_internals(); + } +} + +/////////////////////////////// Rendering ////////////////////////////////////// + +void TileMap::set_cell(int p_layer, const Vector2i &p_coords, int p_source_id, const Vector2i p_atlas_coords, int p_alternative_tile) { + TILEMAP_CALL_FOR_LAYER(p_layer, set_cell, p_coords, p_source_id, p_atlas_coords, p_alternative_tile); +} + +void TileMap::erase_cell(int p_layer, const Vector2i &p_coords) { + TILEMAP_CALL_FOR_LAYER(p_layer, set_cell, p_coords, TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE); +} + +int TileMap::get_cell_source_id(int p_layer, const Vector2i &p_coords, bool p_use_proxies) const { + TILEMAP_CALL_FOR_LAYER_V(p_layer, TileSet::INVALID_SOURCE, get_cell_source_id, p_coords, p_use_proxies); +} + +Vector2i TileMap::get_cell_atlas_coords(int p_layer, const Vector2i &p_coords, bool p_use_proxies) const { + TILEMAP_CALL_FOR_LAYER_V(p_layer, TileSetSource::INVALID_ATLAS_COORDS, get_cell_atlas_coords, p_coords, p_use_proxies); +} + +int TileMap::get_cell_alternative_tile(int p_layer, const Vector2i &p_coords, bool p_use_proxies) const { + TILEMAP_CALL_FOR_LAYER_V(p_layer, TileSetSource::INVALID_TILE_ALTERNATIVE, get_cell_alternative_tile, p_coords, p_use_proxies); +} + +TileData *TileMap::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); +} + +Ref<TileMapPattern> TileMap::get_pattern(int p_layer, TypedArray<Vector2i> p_coords_array) { + TILEMAP_CALL_FOR_LAYER_V(p_layer, Ref<TileMapPattern>(), get_pattern, p_coords_array); +} + +Vector2i TileMap::map_pattern(const Vector2i &p_position_in_tilemap, const Vector2i &p_coords_in_pattern, Ref<TileMapPattern> p_pattern) { + ERR_FAIL_COND_V(p_pattern.is_null(), Vector2i()); + ERR_FAIL_COND_V(!p_pattern->has_cell(p_coords_in_pattern), Vector2i()); + + Vector2i output = p_position_in_tilemap + p_coords_in_pattern; + if (tile_set->get_tile_shape() != TileSet::TILE_SHAPE_SQUARE) { + if (tile_set->get_tile_layout() == TileSet::TILE_LAYOUT_STACKED) { + if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_HORIZONTAL && bool(p_position_in_tilemap.y % 2) && bool(p_coords_in_pattern.y % 2)) { + output.x += 1; + } else if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_VERTICAL && bool(p_position_in_tilemap.x % 2) && bool(p_coords_in_pattern.x % 2)) { + output.y += 1; + } + } else if (tile_set->get_tile_layout() == TileSet::TILE_LAYOUT_STACKED_OFFSET) { + if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_HORIZONTAL && bool(p_position_in_tilemap.y % 2) && bool(p_coords_in_pattern.y % 2)) { + output.x -= 1; + } else if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_VERTICAL && bool(p_position_in_tilemap.x % 2) && bool(p_coords_in_pattern.x % 2)) { + output.y -= 1; } - q_list_element = q_list_element->next(); } } + + return output; +} + +void TileMap::set_pattern(int p_layer, const Vector2i &p_position, const Ref<TileMapPattern> p_pattern) { + TILEMAP_CALL_FOR_LAYER(p_layer, set_pattern, p_position, p_pattern); +} + +HashMap<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_fill_constraints(int p_layer, const Vector<Vector2i> &p_to_replace, int p_terrain_set, const RBSet<TerrainConstraint> &p_constraints) { + HashMap<Vector2i, TileSet::TerrainsPattern> err_value; + TILEMAP_CALL_FOR_LAYER_V(p_layer, err_value, terrain_fill_constraints, p_to_replace, p_terrain_set, p_constraints); +} + +HashMap<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_fill_connect(int p_layer, const Vector<Vector2i> &p_coords_array, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains) { + HashMap<Vector2i, TileSet::TerrainsPattern> err_value; + TILEMAP_CALL_FOR_LAYER_V(p_layer, err_value, terrain_fill_connect, p_coords_array, p_terrain_set, p_terrain, p_ignore_empty_terrains); +} + +HashMap<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_fill_path(int p_layer, const Vector<Vector2i> &p_coords_array, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains) { + HashMap<Vector2i, TileSet::TerrainsPattern> err_value; + TILEMAP_CALL_FOR_LAYER_V(p_layer, err_value, terrain_fill_path, p_coords_array, p_terrain_set, p_terrain, p_ignore_empty_terrains); +} + +HashMap<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_fill_pattern(int p_layer, const Vector<Vector2i> &p_coords_array, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern, bool p_ignore_empty_terrains) { + HashMap<Vector2i, TileSet::TerrainsPattern> err_value; + TILEMAP_CALL_FOR_LAYER_V(p_layer, err_value, terrain_fill_pattern, p_coords_array, p_terrain_set, p_terrains_pattern, p_ignore_empty_terrains); +} + +void TileMap::set_cells_terrain_connect(int p_layer, TypedArray<Vector2i> p_cells, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains) { + TILEMAP_CALL_FOR_LAYER(p_layer, set_cells_terrain_connect, p_cells, p_terrain_set, p_terrain, p_ignore_empty_terrains); +} + +void TileMap::set_cells_terrain_path(int p_layer, TypedArray<Vector2i> p_path, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains) { + TILEMAP_CALL_FOR_LAYER(p_layer, set_cells_terrain_path, p_path, p_terrain_set, p_terrain, p_ignore_empty_terrains); +} + +TileMapCell TileMap::get_cell(int p_layer, const Vector2i &p_coords, bool p_use_proxies) const { + TILEMAP_CALL_FOR_LAYER_V(p_layer, TileMapCell(), get_cell, p_coords, p_use_proxies); +} + +Vector2i TileMap::get_coords_for_body_rid(RID p_physics_body) { + for (const Ref<TileMapLayer> &layer : layers) { + if (layer->has_body_rid(p_physics_body)) { + return layer->get_coords_for_body_rid(p_physics_body); + } + } + ERR_FAIL_V_MSG(Vector2i(), vformat("No tiles for the given body RID %d.", p_physics_body)); +} + +int TileMap::get_layer_for_body_rid(RID p_physics_body) { + for (unsigned int i = 0; i < layers.size(); i++) { + if (layers[i]->has_body_rid(p_physics_body)) { + return i; + } + } + ERR_FAIL_V_MSG(-1, vformat("No tiles for the given body RID %d.", p_physics_body)); +} + +void TileMap::fix_invalid_tiles() { + for (Ref<TileMapLayer> &layer : layers) { + layer->fix_invalid_tiles(); + } +} + +void TileMap::clear_layer(int p_layer) { + TILEMAP_CALL_FOR_LAYER(p_layer, clear) +} + +void TileMap::clear() { + for (Ref<TileMapLayer> &layer : layers) { + layer->clear(); + } +} + +void TileMap::force_update(int p_layer) { + if (p_layer >= 0) { + TILEMAP_CALL_FOR_LAYER(p_layer, force_update); + } else { + _clear_internals(); + _recreate_internals(); + } } #ifdef TOOLS_ENABLED Rect2 TileMap::_edit_get_rect() const { - // Return the visible rect of the tilemap - const_cast<TileMap *>(this)->_recompute_rect_cache(); - return rect_cache; + // Return the visible rect of the tilemap. + if (layers.is_empty()) { + return Rect2(); + } + + bool any_changed = false; + bool changed = false; + Rect2 rect = layers[0]->get_rect(changed); + any_changed |= changed; + for (unsigned int i = 1; i < layers.size(); i++) { + rect = rect.merge(layers[i]->get_rect(changed)); + any_changed |= changed; + } + const_cast<TileMap *>(this)->item_rect_changed(any_changed); + return rect; } #endif @@ -3124,15 +3341,20 @@ bool TileMap::_set(const StringName &p_name, const Variant &p_value) { Vector<String> components = String(p_name).split("/", true, 2); if (p_name == "format") { if (p_value.get_type() == Variant::INT) { - format = (DataFormat)(p_value.operator int64_t()); // Set format used for loading + format = (TileMapLayer::DataFormat)(p_value.operator int64_t()); // Set format used for loading. return true; } } else if (p_name == "tile_data") { // Kept for compatibility reasons. if (p_value.is_array()) { - if (layers.size() < 1) { - layers.resize(1); + if (layers.size() == 0) { + Ref<TileMapLayer> new_layer; + new_layer.instantiate(); + new_layer->set_tile_map(this); + new_layer->set_layer_index_in_tile_map_node(0); + layers.push_back(new_layer); } - _set_tile_data(0, p_value); + layers[0]->set_tile_data(format, p_value); + emit_signal(CoreStringNames::get_singleton()->changed); return true; } return false; @@ -3145,12 +3367,16 @@ bool TileMap::_set(const StringName &p_name, const Variant &p_value) { if (index >= (int)layers.size()) { _clear_internals(); while (index >= (int)layers.size()) { - layers.push_back(TileMapLayer()); + Ref<TileMapLayer> new_layer; + new_layer.instantiate(); + new_layer->set_tile_map(this); + new_layer->set_layer_index_in_tile_map_node(index); + layers.push_back(new_layer); } _recreate_internals(); notify_property_list_changed(); - emit_signal(SNAME("changed")); + emit_signal(CoreStringNames::get_singleton()->changed); update_configuration_warnings(); } @@ -3173,7 +3399,8 @@ bool TileMap::_set(const StringName &p_name, const Variant &p_value) { set_layer_z_index(index, p_value); return true; } else if (components[1] == "tile_data") { - _set_tile_data(index, p_value); + layers[index]->set_tile_data(format, p_value); + emit_signal(CoreStringNames::get_singleton()->changed); return true; } else { return false; @@ -3185,7 +3412,7 @@ bool TileMap::_set(const StringName &p_name, const Variant &p_value) { bool TileMap::_get(const StringName &p_name, Variant &r_ret) const { Vector<String> components = String(p_name).split("/", true, 2); if (p_name == "format") { - r_ret = FORMAT_3; // When saving, always save highest format + r_ret = TileMapLayer::FORMAT_MAX - 1; // When saving, always save highest format. return true; } else if (components.size() == 2 && components[0].begins_with("layer_") && components[0].trim_prefix("layer_").is_valid_int()) { int index = components[0].trim_prefix("layer_").to_int(); @@ -3212,7 +3439,7 @@ bool TileMap::_get(const StringName &p_name, Variant &r_ret) const { r_ret = get_layer_z_index(index); return true; } else if (components[1] == "tile_data") { - r_ret = _get_tile_data(index); + r_ret = layers[index]->get_tile_data(); return true; } else { return false; @@ -3236,7 +3463,7 @@ void TileMap::_get_property_list(List<PropertyInfo> *p_list) const { } Vector2 TileMap::map_to_local(const Vector2i &p_pos) const { - // SHOULD RETURN THE CENTER OF THE CELL + // SHOULD RETURN THE CENTER OF THE CELL. ERR_FAIL_COND_V(!tile_set.is_valid(), Vector2()); Vector2 ret = p_pos; @@ -3245,7 +3472,7 @@ Vector2 TileMap::map_to_local(const Vector2i &p_pos) const { if (tile_shape == TileSet::TILE_SHAPE_HALF_OFFSET_SQUARE || tile_shape == TileSet::TILE_SHAPE_HEXAGON || tile_shape == TileSet::TILE_SHAPE_ISOMETRIC) { // Technically, those 3 shapes are equivalent, as they are basically half-offset, but with different levels or overlap. - // square = no overlap, hexagon = 0.25 overlap, isometric = 0.5 overlap + // square = no overlap, hexagon = 0.25 overlap, isometric = 0.5 overlap. if (tile_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { switch (tile_set->get_tile_layout()) { case TileSet::TILE_LAYOUT_STACKED: @@ -3267,7 +3494,7 @@ Vector2 TileMap::map_to_local(const Vector2i &p_pos) const { ret = Vector2((ret.x - ret.y) / 2, ret.y + ret.x); break; } - } else { // TILE_OFFSET_AXIS_VERTICAL + } else { // TILE_OFFSET_AXIS_VERTICAL. switch (tile_set->get_tile_layout()) { case TileSet::TILE_LAYOUT_STACKED: ret = Vector2(ret.x, ret.y + (Math::posmod(ret.x, 2) == 0 ? 0.0 : 0.5)); @@ -3291,7 +3518,7 @@ Vector2 TileMap::map_to_local(const Vector2i &p_pos) const { } } - // Multiply by the overlapping ratio + // Multiply by the overlapping ratio. double overlapping_ratio = 1.0; if (tile_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC) { @@ -3300,7 +3527,7 @@ Vector2 TileMap::map_to_local(const Vector2i &p_pos) const { overlapping_ratio = 0.75; } ret.y *= overlapping_ratio; - } else { // TILE_OFFSET_AXIS_VERTICAL + } else { // TILE_OFFSET_AXIS_VERTICAL. if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC) { overlapping_ratio = 0.5; } else if (tile_shape == TileSet::TILE_SHAPE_HEXAGON) { @@ -3322,7 +3549,7 @@ Vector2i TileMap::local_to_map(const Vector2 &p_local_position) const { TileSet::TileOffsetAxis tile_offset_axis = tile_set->get_tile_offset_axis(); TileSet::TileLayout tile_layout = tile_set->get_tile_layout(); - // Divide by the overlapping ratio + // Divide by the overlapping ratio. double overlapping_ratio = 1.0; if (tile_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC) { @@ -3331,7 +3558,7 @@ Vector2i TileMap::local_to_map(const Vector2 &p_local_position) const { overlapping_ratio = 0.75; } ret.y /= overlapping_ratio; - } else { // TILE_OFFSET_AXIS_VERTICAL + } else { // TILE_OFFSET_AXIS_VERTICAL. if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC) { overlapping_ratio = 0.5; } else if (tile_shape == TileSet::TILE_SHAPE_HEXAGON) { @@ -3343,7 +3570,7 @@ Vector2i TileMap::local_to_map(const Vector2 &p_local_position) const { // For each half-offset shape, we check if we are in the corner of the tile, and thus should correct the local position accordingly. if (tile_shape == TileSet::TILE_SHAPE_HALF_OFFSET_SQUARE || tile_shape == TileSet::TILE_SHAPE_HEXAGON || tile_shape == TileSet::TILE_SHAPE_ISOMETRIC) { // Technically, those 3 shapes are equivalent, as they are basically half-offset, but with different levels or overlap. - // square = no overlap, hexagon = 0.25 overlap, isometric = 0.5 overlap + // square = no overlap, hexagon = 0.25 overlap, isometric = 0.5 overlap. if (tile_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { // Smart floor of the position Vector2 raw_pos = ret; @@ -3353,7 +3580,7 @@ Vector2i TileMap::local_to_map(const Vector2 &p_local_position) const { ret = ret.floor(); } - // Compute the tile offset, and if we might the output for a neighbor top tile + // Compute the tile offset, and if we might the output for a neighbor top tile. Vector2 in_tile_pos = raw_pos - ret; bool in_top_left_triangle = (in_tile_pos - Vector2(0.5, 0.0)).cross(Vector2(-0.5, 1.0 / overlapping_ratio - 1)) <= 0; bool in_top_right_triangle = (in_tile_pos - Vector2(0.5, 0.0)).cross(Vector2(0.5, 1.0 / overlapping_ratio - 1)) > 0; @@ -3408,8 +3635,8 @@ Vector2i TileMap::local_to_map(const Vector2 &p_local_position) const { } break; } - } else { // TILE_OFFSET_AXIS_VERTICAL - // Smart floor of the position + } else { // TILE_OFFSET_AXIS_VERTICAL. + // Smart floor of the position. Vector2 raw_pos = ret; if (Math::posmod(Math::floor(ret.x), 2) ^ (tile_layout == TileSet::TILE_LAYOUT_STACKED_OFFSET)) { ret = Vector2(Math::floor(ret.x), Math::floor(ret.y + 0.5) - 0.5); @@ -3417,7 +3644,7 @@ Vector2i TileMap::local_to_map(const Vector2 &p_local_position) const { ret = ret.floor(); } - // Compute the tile offset, and if we might the output for a neighbor top tile + // Compute the tile offset, and if we might the output for a neighbor top tile. Vector2 in_tile_pos = raw_pos - ret; bool in_top_left_triangle = (in_tile_pos - Vector2(0.0, 0.5)).cross(Vector2(1.0 / overlapping_ratio - 1, -0.5)) > 0; bool in_bottom_left_triangle = (in_tile_pos - Vector2(0.0, 0.5)).cross(Vector2(1.0 / overlapping_ratio - 1, 0.5)) <= 0; @@ -3546,7 +3773,7 @@ Vector2i TileMap::get_neighbor_cell(const Vector2i &p_coords, TileSet::CellNeigh default: ERR_FAIL_V(p_coords); } - } else { // Half-offset shapes (square and hexagon) + } else { // Half-offset shapes (square and hexagon). switch (tile_set->get_tile_layout()) { case TileSet::TILE_LAYOUT_STACKED: { if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { @@ -3856,63 +4083,23 @@ Vector2i TileMap::get_neighbor_cell(const Vector2i &p_coords, TileSet::CellNeigh } TypedArray<Vector2i> TileMap::get_used_cells(int p_layer) const { - ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), TypedArray<Vector2i>()); - - // Returns the cells used in the tilemap. - TypedArray<Vector2i> a; - a.resize(layers[p_layer].tile_map.size()); - int i = 0; - for (const KeyValue<Vector2i, TileMapCell> &E : layers[p_layer].tile_map) { - Vector2i p(E.key.x, E.key.y); - a[i++] = p; - } - - return a; + TILEMAP_CALL_FOR_LAYER_V(p_layer, TypedArray<Vector2i>(), get_used_cells); } TypedArray<Vector2i> TileMap::get_used_cells_by_id(int p_layer, int p_source_id, const Vector2i p_atlas_coords, int p_alternative_tile) const { - ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), TypedArray<Vector2i>()); - - // Returns the cells used in the tilemap. - TypedArray<Vector2i> a; - for (const KeyValue<Vector2i, TileMapCell> &E : layers[p_layer].tile_map) { - if ((p_source_id == TileSet::INVALID_SOURCE || p_source_id == E.value.source_id) && - (p_atlas_coords == TileSetSource::INVALID_ATLAS_COORDS || p_atlas_coords == E.value.get_atlas_coords()) && - (p_alternative_tile == TileSetSource::INVALID_TILE_ALTERNATIVE || p_alternative_tile == E.value.alternative_tile)) { - a.push_back(E.key); - } - } - - return a; + TILEMAP_CALL_FOR_LAYER_V(p_layer, TypedArray<Vector2i>(), get_used_cells_by_id); } -Rect2i TileMap::get_used_rect() { // Not const because of cache - // Return the rect of the currently used area - if (used_rect_cache_dirty) { - bool first = true; - used_rect_cache = Rect2i(); - - for (unsigned int i = 0; i < layers.size(); i++) { - const HashMap<Vector2i, TileMapCell> &tile_map = layers[i].tile_map; - if (tile_map.size() > 0) { - if (first) { - used_rect_cache = Rect2i(tile_map.begin()->key.x, tile_map.begin()->key.y, 0, 0); - first = false; - } - - for (const KeyValue<Vector2i, TileMapCell> &E : tile_map) { - used_rect_cache.expand_to(Vector2i(E.key.x, E.key.y)); - } - } - } - - if (!first) { // first is true if every layer is empty. - used_rect_cache.size += Vector2i(1, 1); // The cache expands to top-left coordinate, so we add one full tile. - } - used_rect_cache_dirty = false; +Rect2i TileMap::get_used_rect() const { + // Return the visible rect of the tilemap. + if (layers.is_empty()) { + return Rect2i(); } - - return used_rect_cache; + Rect2 rect = layers[0]->get_used_rect(); + for (unsigned int i = 1; i < layers.size(); i++) { + rect = rect.merge(layers[i]->get_used_rect()); + } + return rect; } // --- Override some methods of the CanvasItem class to pass the changes to the quadrants CanvasItems --- @@ -3920,13 +4107,8 @@ Rect2i TileMap::get_used_rect() { // Not const because of cache void TileMap::set_light_mask(int p_light_mask) { // Occlusion: set light mask. CanvasItem::set_light_mask(p_light_mask); - for (unsigned int layer = 0; layer < layers.size(); layer++) { - for (const KeyValue<Vector2i, TileMapQuadrant> &E : layers[layer].quadrant_map) { - for (const RID &ci : E.value.canvas_items) { - RenderingServer::get_singleton()->canvas_item_set_light_mask(ci, get_light_mask()); - } - } - _rendering_update_layer(layer); + for (Ref<TileMapLayer> &layer : layers) { + layer->notify_light_mask_changed(); } } @@ -3935,14 +4117,8 @@ void TileMap::set_material(const Ref<Material> &p_material) { CanvasItem::set_material(p_material); // Update material for the whole tilemap. - for (unsigned int layer = 0; layer < layers.size(); layer++) { - for (KeyValue<Vector2i, TileMapQuadrant> &E : layers[layer].quadrant_map) { - TileMapQuadrant &q = E.value; - for (const RID &ci : q.canvas_items) { - RS::get_singleton()->canvas_item_set_use_parent_material(ci, get_use_parent_material() || get_material().is_valid()); - } - } - _rendering_update_layer(layer); + for (Ref<TileMapLayer> &layer : layers) { + layer->notify_material_changed(); } } @@ -3951,46 +4127,24 @@ void TileMap::set_use_parent_material(bool p_use_parent_material) { CanvasItem::set_use_parent_material(p_use_parent_material); // Update use_parent_material for the whole tilemap. - for (unsigned int layer = 0; layer < layers.size(); layer++) { - for (KeyValue<Vector2i, TileMapQuadrant> &E : layers[layer].quadrant_map) { - TileMapQuadrant &q = E.value; - for (const RID &ci : q.canvas_items) { - RS::get_singleton()->canvas_item_set_use_parent_material(ci, get_use_parent_material() || get_material().is_valid()); - } - } - _rendering_update_layer(layer); + for (Ref<TileMapLayer> &layer : layers) { + layer->notify_use_parent_material_changed(); } } void TileMap::set_texture_filter(TextureFilter p_texture_filter) { // Set a default texture filter for the whole tilemap. CanvasItem::set_texture_filter(p_texture_filter); - TextureFilter target_filter = get_texture_filter_in_tree(); - for (unsigned int layer = 0; layer < layers.size(); layer++) { - for (HashMap<Vector2i, TileMapQuadrant>::Iterator F = layers[layer].quadrant_map.begin(); F; ++F) { - TileMapQuadrant &q = F->value; - for (const RID &ci : q.canvas_items) { - RenderingServer::get_singleton()->canvas_item_set_default_texture_filter(ci, RS::CanvasItemTextureFilter(target_filter)); - _make_quadrant_dirty(F); - } - } - _rendering_update_layer(layer); + for (Ref<TileMapLayer> &layer : layers) { + layer->notify_texture_filter_changed(); } } void TileMap::set_texture_repeat(CanvasItem::TextureRepeat p_texture_repeat) { // Set a default texture repeat for the whole tilemap. CanvasItem::set_texture_repeat(p_texture_repeat); - TextureRepeat target_repeat = get_texture_repeat_in_tree(); - for (unsigned int layer = 0; layer < layers.size(); layer++) { - for (HashMap<Vector2i, TileMapQuadrant>::Iterator F = layers[layer].quadrant_map.begin(); F; ++F) { - TileMapQuadrant &q = F->value; - for (const RID &ci : q.canvas_items) { - RenderingServer::get_singleton()->canvas_item_set_default_texture_repeat(ci, RS::CanvasItemTextureRepeat(target_repeat)); - _make_quadrant_dirty(F); - } - } - _rendering_update_layer(layer); + for (Ref<TileMapLayer> &layer : layers) { + layer->notify_texture_repeat_changed(); } } @@ -4088,15 +4242,15 @@ PackedStringArray TileMap::get_configuration_warnings() const { // Retrieve the set of Z index values with a Y-sorted layer. RBSet<int> y_sorted_z_index; - for (const TileMapLayer &layer : layers) { - if (layer.y_sort_enabled) { - y_sorted_z_index.insert(layer.z_index); + for (const Ref<TileMapLayer> &layer : layers) { + if (layer->is_y_sort_enabled()) { + y_sorted_z_index.insert(layer->get_z_index()); } } // Check if we have a non-sorted layer in a Z-index with a Y-sorted layer. - for (const TileMapLayer &layer : layers) { - if (!layer.y_sort_enabled && y_sorted_z_index.has(layer.z_index)) { + for (const Ref<TileMapLayer> &layer : layers) { + if (!layer->is_y_sort_enabled() && y_sorted_z_index.has(layer->get_z_index())) { warnings.push_back(RTR("A Y-sorted layer has the same Z-index value as a not Y-sorted layer.\nThis may lead to unwanted behaviors, as a layer that is not Y-sorted will be Y-sorted as a whole with tiles from Y-sorted layers.")); break; } @@ -4104,8 +4258,8 @@ PackedStringArray TileMap::get_configuration_warnings() const { // Check if Y-sort is enabled on a layer but not on the node. if (!is_y_sort_enabled()) { - for (const TileMapLayer &layer : layers) { - if (layer.y_sort_enabled) { + for (const Ref<TileMapLayer> &layer : layers) { + if (layer->is_y_sort_enabled()) { warnings.push_back(RTR("A TileMap layer is set as Y-sorted, but Y-sort is not enabled on the TileMap node itself.")); break; } @@ -4116,8 +4270,8 @@ PackedStringArray TileMap::get_configuration_warnings() const { if (tile_set.is_valid() && tile_set->get_tile_shape() == TileSet::TILE_SHAPE_ISOMETRIC) { bool warn = !is_y_sort_enabled(); if (!warn) { - for (const TileMapLayer &layer : layers) { - if (!layer.y_sort_enabled) { + for (const Ref<TileMapLayer> &layer : layers) { + if (!layer->is_y_sort_enabled()) { warn = true; break; } @@ -4155,6 +4309,13 @@ void TileMap::_bind_methods() { ClassDB::bind_method(D_METHOD("get_layer_y_sort_origin", "layer"), &TileMap::get_layer_y_sort_origin); ClassDB::bind_method(D_METHOD("set_layer_z_index", "layer", "z_index"), &TileMap::set_layer_z_index); ClassDB::bind_method(D_METHOD("get_layer_z_index", "layer"), &TileMap::get_layer_z_index); + ClassDB::bind_method(D_METHOD("set_layer_navigation_map", "layer", "map"), &TileMap::set_layer_navigation_map); + ClassDB::bind_method(D_METHOD("get_layer_navigation_map", "layer"), &TileMap::get_layer_navigation_map); + +#ifndef DISABLE_DEPRECATED + ClassDB::bind_method(D_METHOD("set_navigation_map", "layer", "map"), &TileMap::set_layer_navigation_map); + ClassDB::bind_method(D_METHOD("get_navigation_map", "layer"), &TileMap::get_layer_navigation_map); +#endif // DISABLE_DEPRECATED ClassDB::bind_method(D_METHOD("set_collision_animatable", "enabled"), &TileMap::set_collision_animatable); ClassDB::bind_method(D_METHOD("is_collision_animatable"), &TileMap::is_collision_animatable); @@ -4164,9 +4325,6 @@ void TileMap::_bind_methods() { ClassDB::bind_method(D_METHOD("set_navigation_visibility_mode", "navigation_visibility_mode"), &TileMap::set_navigation_visibility_mode); ClassDB::bind_method(D_METHOD("get_navigation_visibility_mode"), &TileMap::get_navigation_visibility_mode); - ClassDB::bind_method(D_METHOD("set_navigation_map", "layer", "map"), &TileMap::set_navigation_map); - ClassDB::bind_method(D_METHOD("get_navigation_map", "layer"), &TileMap::get_navigation_map); - ClassDB::bind_method(D_METHOD("set_cell", "layer", "coords", "source_id", "atlas_coords", "alternative_tile"), &TileMap::set_cell, DEFVAL(TileSet::INVALID_SOURCE), DEFVAL(TileSetSource::INVALID_ATLAS_COORDS), DEFVAL(0)); ClassDB::bind_method(D_METHOD("erase_cell", "layer", "coords"), &TileMap::erase_cell); ClassDB::bind_method(D_METHOD("get_cell_source_id", "layer", "coords", "use_proxies"), &TileMap::get_cell_source_id, DEFVAL(false)); @@ -4216,9 +4374,9 @@ void TileMap::_bind_methods() { ADD_ARRAY("layers", "layer_"); - ADD_PROPERTY_DEFAULT("format", FORMAT_1); + ADD_PROPERTY_DEFAULT("format", TileMapLayer::FORMAT_1); - ADD_SIGNAL(MethodInfo("changed")); + ADD_SIGNAL(MethodInfo(CoreStringNames::get_singleton()->changed)); BIND_ENUM_CONSTANT(VISIBILITY_MODE_DEFAULT); BIND_ENUM_CONSTANT(VISIBILITY_MODE_FORCE_HIDE); @@ -4226,9 +4384,11 @@ void TileMap::_bind_methods() { } void TileMap::_tile_set_changed() { - emit_signal(SNAME("changed")); + emit_signal(CoreStringNames::get_singleton()->changed); _tile_set_changed_deferred_update_needed = true; - instantiated_scenes.clear(); + for (Ref<TileMapLayer> &layer : layers) { + layer->clear_instantiated_scenes(); + } call_deferred(SNAME("_tile_set_changed_deferred_update")); update_configuration_warnings(); } @@ -4245,7 +4405,11 @@ TileMap::TileMap() { set_notify_transform(true); set_notify_local_transform(false); - layers.resize(1); + Ref<TileMapLayer> new_layer; + new_layer.instantiate(); + new_layer->set_tile_map(this); + new_layer->set_layer_index_in_tile_map_node(0); + layers.push_back(new_layer); } TileMap::~TileMap() { @@ -4255,3 +4419,6 @@ TileMap::~TileMap() { _clear_internals(); } + +#undef TILEMAP_CALL_FOR_LAYER +#undef TILEMAP_CALL_FOR_LAYER_V |