diff options
Diffstat (limited to 'scene/2d/tile_map.cpp')
-rw-r--r-- | scene/2d/tile_map.cpp | 485 |
1 files changed, 308 insertions, 177 deletions
diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index 5ba8c95a06..40e8818262 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -34,15 +34,19 @@ #include "scene/resources/world_2d.h" #include "servers/navigation_server_2d.h" +#ifdef DEBUG_ENABLED +#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> tile_set = tile_map->get_tileset(); - ERR_FAIL_COND_V(!tile_set.is_valid(), output); + Ref<TileSet> ts = tile_map->get_tileset(); + ERR_FAIL_COND_V(!ts.is_valid(), output); - TileSet::TileShape shape = tile_set->get_tile_shape(); + TileSet::TileShape shape = ts->get_tile_shape(); if (shape == TileSet::TILE_SHAPE_SQUARE) { switch (bit) { case 1: @@ -83,7 +87,7 @@ HashMap<Vector2i, TileSet::CellNeighbor> TileMap::TerrainConstraint::get_overlap } } else { // Half offset shapes. - TileSet::TileOffsetAxis offset_axis = tile_set->get_tile_offset_axis(); + TileSet::TileOffsetAxis offset_axis = ts->get_tile_offset_axis(); if (offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { switch (bit) { case 1: @@ -146,8 +150,8 @@ HashMap<Vector2i, TileSet::CellNeighbor> TileMap::TerrainConstraint::get_overlap TileMap::TerrainConstraint::TerrainConstraint(const TileMap *p_tile_map, const Vector2i &p_position, int p_terrain) { tile_map = p_tile_map; - Ref<TileSet> tile_set = tile_map->get_tileset(); - ERR_FAIL_COND(!tile_set.is_valid()); + Ref<TileSet> ts = tile_map->get_tileset(); + ERR_FAIL_COND(!ts.is_valid()); bit = 0; base_cell_coords = p_position; @@ -158,10 +162,10 @@ TileMap::TerrainConstraint::TerrainConstraint(const TileMap *p_tile_map, const V // The way we build the constraint make it easy to detect conflicting constraints. tile_map = p_tile_map; - Ref<TileSet> tile_set = tile_map->get_tileset(); - ERR_FAIL_COND(!tile_set.is_valid()); + Ref<TileSet> ts = tile_map->get_tileset(); + ERR_FAIL_COND(!ts.is_valid()); - TileSet::TileShape shape = tile_set->get_tile_shape(); + TileSet::TileShape shape = ts->get_tile_shape(); if (shape == TileSet::TILE_SHAPE_SQUARE) { switch (p_bit) { case TileSet::CELL_NEIGHBOR_RIGHT_SIDE: @@ -240,7 +244,7 @@ TileMap::TerrainConstraint::TerrainConstraint(const TileMap *p_tile_map, const V } } else { // Half-offset shapes - TileSet::TileOffsetAxis offset_axis = tile_set->get_tile_offset_axis(); + 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: @@ -553,7 +557,7 @@ int TileMap::get_layers_count() const { void TileMap::add_layer(int p_to_pos) { if (p_to_pos < 0) { - p_to_pos = layers.size(); + p_to_pos = layers.size() + p_to_pos + 1; } ERR_FAIL_INDEX(p_to_pos, (int)layers.size() + 1); @@ -612,6 +616,9 @@ void TileMap::remove_layer(int p_layer) { } 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()); layers[p_layer].name = p_name; emit_signal(SNAME("changed")); @@ -623,6 +630,9 @@ String TileMap::get_layer_name(int p_layer) const { } 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()); layers[p_layer].enabled = p_enabled; _clear_layer_internals(p_layer); @@ -638,6 +648,9 @@ bool TileMap::is_layer_enabled(int p_layer) const { } 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()); layers[p_layer].modulate = p_modulate; _clear_layer_internals(p_layer); @@ -651,6 +664,9 @@ Color TileMap::get_layer_modulate(int p_layer) const { } 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()); layers[p_layer].y_sort_enabled = p_y_sort_enabled; _clear_layer_internals(p_layer); @@ -666,6 +682,9 @@ bool TileMap::is_layer_y_sort_enabled(int p_layer) const { } 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()); layers[p_layer].y_sort_origin = p_y_sort_origin; _clear_layer_internals(p_layer); @@ -679,6 +698,9 @@ int TileMap::get_layer_y_sort_origin(int p_layer) const { } 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()); layers[p_layer].z_index = p_z_index; _clear_layer_internals(p_layer); @@ -736,12 +758,12 @@ void TileMap::set_y_sort_enabled(bool p_enable) { } Vector2i TileMap::_coords_to_quadrant_coords(int p_layer, const Vector2i &p_coords) const { - int quadrant_size = get_effective_quadrant_size(p_layer); + int quad_size = get_effective_quadrant_size(p_layer); // Rounding down, instead of simply rounding towards zero (truncating) return Vector2i( - p_coords.x > 0 ? p_coords.x / quadrant_size : (p_coords.x - (quadrant_size - 1)) / quadrant_size, - p_coords.y > 0 ? p_coords.y / quadrant_size : (p_coords.y - (quadrant_size - 1)) / quadrant_size); + 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) { @@ -810,13 +832,13 @@ void TileMap::_update_dirty_quadrants() { // Update the coords cache. for (SelfList<TileMapQuadrant> *q = dirty_quadrant_list.first(); q; q = q->next()) { - q->self()->map_to_world.clear(); - q->self()->world_to_map.clear(); + q->self()->map_to_local.clear(); + q->self()->local_to_map.clear(); for (const Vector2i &E : q->self()->cells) { Vector2i pk = E; - Vector2i pk_world_coords = map_to_world(pk); - q->self()->map_to_world[pk] = pk_world_coords; - q->self()->world_to_map[pk_world_coords] = pk; + Vector2i 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; } } @@ -834,7 +856,7 @@ void TileMap::_update_dirty_quadrants() { for (SelfList<TileMapQuadrant> *q = dirty_quadrant_list.first(); q; q = q->next()) { rs->canvas_item_clear(q->self()->debug_canvas_item); Transform2D xform; - xform.set_origin(map_to_world(q->self()->coords * get_effective_quadrant_size(layer))); + 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()); @@ -960,10 +982,10 @@ void TileMap::_recompute_rect_cache() { 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_world(E.key * get_effective_quadrant_size(layer)); - r.expand_to(map_to_world((E.key + Vector2i(1, 0)) * get_effective_quadrant_size(layer))); - r.expand_to(map_to_world((E.key + Vector2i(1, 1)) * get_effective_quadrant_size(layer))); - r.expand_to(map_to_world((E.key + Vector2i(0, 1)) * get_effective_quadrant_size(layer))); + 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; @@ -985,18 +1007,34 @@ void TileMap::_recompute_rect_cache() { void TileMap::_rendering_notification(int p_what) { switch (p_what) { + case NOTIFICATION_ENTER_CANVAS: { + bool node_visible = is_visible_in_tree(); + for (int layer = 0; layer < (int)layers.size(); layer++) { + for (KeyValue<Vector2i, TileMapQuadrant> &E_quadrant : layers[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 visible = is_visible_in_tree(); + bool node_visible = is_visible_in_tree(); for (int layer = 0; layer < (int)layers.size(); layer++) { for (KeyValue<Vector2i, TileMapQuadrant> &E_quadrant : layers[layer].quadrant_map) { TileMapQuadrant &q = E_quadrant.value; // Update occluders transform. - for (const KeyValue<Vector2i, Vector2i> &E_cell : q.world_to_map) { + for (const KeyValue<Vector2i, Vector2i> &E_cell : q.local_to_map) { Transform2D xform; xform.set_origin(E_cell.key); - for (const RID &occluder : q.occluders) { - RS::get_singleton()->canvas_light_occluder_set_enabled(occluder, visible); + for (const KeyValue<Vector2i, RID> &kv : q.occluders) { + RS::get_singleton()->canvas_light_occluder_set_enabled(kv.value, node_visible); } } } @@ -1012,12 +1050,10 @@ void TileMap::_rendering_notification(int p_what) { TileMapQuadrant &q = E_quadrant.value; // Update occluders transform. - for (const KeyValue<Vector2i, Vector2i> &E_cell : q.world_to_map) { + for (const KeyValue<Vector2i, RID> &kv : q.occluders) { Transform2D xform; - xform.set_origin(E_cell.key); - for (const RID &occluder : q.occluders) { - RS::get_singleton()->canvas_light_occluder_set_transform(occluder, get_global_transform() * xform); - } + xform.set_origin(map_to_local(kv.key)); + RenderingServer::get_singleton()->canvas_light_occluder_set_transform(kv.value, get_global_transform() * xform); } } } @@ -1028,6 +1064,17 @@ void TileMap::_rendering_notification(int p_what) { RenderingServer::get_singleton()->canvas_item_set_sort_children_by_y(get_canvas_item(), is_y_sort_enabled()); } } break; + + case NOTIFICATION_EXIT_CANVAS: { + for (int layer = 0; layer < (int)layers.size(); layer++) { + for (KeyValue<Vector2i, TileMapQuadrant> &E_quadrant : layers[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; } } @@ -1050,8 +1097,8 @@ void TileMap::_rendering_update_layer(int p_layer) { 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())); - rs->canvas_item_set_default_texture_repeat(ci, RS::CanvasItemTextureRepeat(get_texture_repeat())); + 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()); } @@ -1069,7 +1116,7 @@ void TileMap::_rendering_update_dirty_quadrants(SelfList<TileMapQuadrant>::List ERR_FAIL_COND(!is_inside_tree()); ERR_FAIL_COND(!tile_set.is_valid()); - bool visible = is_visible_in_tree(); + bool node_visible = is_visible_in_tree(); SelfList<TileMapQuadrant> *q_list_element = r_dirty_quadrant_list.first(); while (q_list_element) { @@ -1084,31 +1131,31 @@ void TileMap::_rendering_update_dirty_quadrants(SelfList<TileMapQuadrant>::List q.canvas_items.clear(); // Free the occluders. - for (const RID &occluder : q.occluders) { - rs->free(occluder); + for (const KeyValue<Vector2i, RID> &kv : q.occluders) { + rs->free(kv.value); } q.occluders.clear(); // Those allow to group cell per material or z-index. Ref<Material> prev_material; int prev_z_index = 0; - RID prev_canvas_item; + RID prev_ci; - Color modulate = get_self_modulate(); - modulate *= get_layer_modulate(q.layer); + Color tile_modulate = get_self_modulate(); + tile_modulate *= get_layer_modulate(q.layer); if (selected_layer >= 0) { int z1 = get_layer_z_index(q.layer); int z2 = get_layer_z_index(selected_layer); if (z1 < z2 || (z1 == z2 && q.layer < selected_layer)) { - modulate = modulate.darkened(0.5); + tile_modulate = tile_modulate.darkened(0.5); } else if (z1 > z2 || (z1 == z2 && q.layer > selected_layer)) { - modulate = modulate.darkened(0.5); - modulate.a *= 0.3; + tile_modulate = tile_modulate.darkened(0.5); + tile_modulate.a *= 0.3; } } // Iterate over the cells of the quadrant. - for (const KeyValue<Vector2i, Vector2i> &E_cell : q.world_to_map) { + for (const KeyValue<Vector2i, Vector2i> &E_cell : q.local_to_map) { TileMapCell c = get_cell(q.layer, E_cell.value, true); TileSetSource *source; @@ -1130,53 +1177,53 @@ void TileMap::_rendering_update_dirty_quadrants(SelfList<TileMapQuadrant>::List } Ref<Material> mat = tile_data->get_material(); - int z_index = tile_data->get_z_index(); + int tile_z_index = tile_data->get_z_index(); // Quandrant pos. - Vector2 position = map_to_world(q.coords * get_effective_quadrant_size(q.layer)); + 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) { // When Y-sorting, the quandrant size is sure to be 1, we can thus offset the CanvasItem. - position.y += layers[q.layer].y_sort_origin + tile_data->get_y_sort_origin(); + tile_position.y += layers[q.layer].y_sort_origin + tile_data->get_y_sort_origin(); } // --- CanvasItems --- // Create two canvas items, for rendering and debug. - RID canvas_item; + RID ci; // Check if the material or the z_index changed. - if (prev_canvas_item == RID() || prev_material != mat || prev_z_index != z_index) { + if (prev_ci == RID() || prev_material != mat || prev_z_index != tile_z_index) { // If so, create a new CanvasItem. - canvas_item = rs->canvas_item_create(); + ci = rs->canvas_item_create(); if (mat.is_valid()) { - rs->canvas_item_set_material(canvas_item, mat->get_rid()); + rs->canvas_item_set_material(ci, mat->get_rid()); } - rs->canvas_item_set_parent(canvas_item, layers[q.layer].canvas_item); - rs->canvas_item_set_use_parent_material(canvas_item, get_use_parent_material() || get_material().is_valid()); + 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()); Transform2D xform; - xform.set_origin(position); - rs->canvas_item_set_transform(canvas_item, xform); + xform.set_origin(tile_position); + rs->canvas_item_set_transform(ci, xform); - rs->canvas_item_set_light_mask(canvas_item, get_light_mask()); - rs->canvas_item_set_z_as_relative_to_parent(canvas_item, true); - rs->canvas_item_set_z_index(canvas_item, z_index); + rs->canvas_item_set_light_mask(ci, 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(canvas_item, RS::CanvasItemTextureFilter(get_texture_filter())); - rs->canvas_item_set_default_texture_repeat(canvas_item, RS::CanvasItemTextureRepeat(get_texture_repeat())); + 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())); - q.canvas_items.push_back(canvas_item); + q.canvas_items.push_back(ci); - prev_canvas_item = canvas_item; + prev_ci = ci; prev_material = mat; - prev_z_index = z_index; + prev_z_index = tile_z_index; } else { // Keep the same canvas_item to draw on. - canvas_item = prev_canvas_item; + ci = prev_ci; } // Drawing the tile in the canvas item. - draw_tile(canvas_item, E_cell.key - position, tile_set, c.source_id, c.get_atlas_coords(), c.alternative_tile, -1, modulate, tile_data); + draw_tile(ci, E_cell.key - tile_position, tile_set, c.source_id, c.get_atlas_coords(), c.alternative_tile, -1, tile_modulate, tile_data); // --- Occluders --- for (int i = 0; i < tile_set->get_occlusion_layers_count(); i++) { @@ -1184,12 +1231,12 @@ void TileMap::_rendering_update_dirty_quadrants(SelfList<TileMapQuadrant>::List xform.set_origin(E_cell.key); if (tile_data->get_occluder(i).is_valid()) { RID occluder_id = rs->canvas_light_occluder_create(); - rs->canvas_light_occluder_set_enabled(occluder_id, visible); + rs->canvas_light_occluder_set_enabled(occluder_id, node_visible); rs->canvas_light_occluder_set_transform(occluder_id, 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_set_light_mask(occluder_id, tile_set->get_occlusion_layer_light_mask(i)); - q.occluders.push_back(occluder_id); + q.occluders[E_cell.value] = occluder_id; } } } @@ -1205,14 +1252,14 @@ void TileMap::_rendering_update_dirty_quadrants(SelfList<TileMapQuadrant>::List int index = -(int64_t)0x80000000; //always must be drawn below children. for (int layer = 0; layer < (int)layers.size(); layer++) { - // Sort the quadrants coords per world coordinates - RBMap<Vector2i, Vector2i, TileMapQuadrant::CoordsWorldComparator> world_to_map; + // Sort the quadrants coords per local coordinates. + RBMap<Vector2i, Vector2i, TileMapQuadrant::CoordsWorldComparator> local_to_map; for (const KeyValue<Vector2i, TileMapQuadrant> &E : layers[layer].quadrant_map) { - world_to_map[map_to_world(E.key)] = E.key; + local_to_map[map_to_local(E.key)] = E.key; } - // Sort the quadrants - for (const KeyValue<Vector2i, Vector2i> &E : world_to_map) { + // Sort the quadrants. + for (const KeyValue<Vector2i, Vector2i> &E : local_to_map) { TileMapQuadrant &q = layers[layer].quadrant_map[E.value]; for (const RID &ci : q.canvas_items) { RS::get_singleton()->canvas_item_set_draw_index(ci, index++); @@ -1237,8 +1284,8 @@ void TileMap::_rendering_cleanup_quadrant(TileMapQuadrant *p_quadrant) { p_quadrant->canvas_items.clear(); // Free the occluders. - for (const RID &occluder : p_quadrant->occluders) { - RenderingServer::get_singleton()->free(occluder); + for (const KeyValue<Vector2i, RID> &kv : p_quadrant->occluders) { + RenderingServer::get_singleton()->free(kv.value); } p_quadrant->occluders.clear(); } @@ -1250,9 +1297,9 @@ void TileMap::_rendering_draw_quadrant_debug(TileMapQuadrant *p_quadrant) { return; } - // Draw a placeholder for scenes needing one. + // Draw a placeholder for tiles needing one. RenderingServer *rs = RenderingServer::get_singleton(); - Vector2 quadrant_pos = map_to_world(p_quadrant->coords * get_effective_quadrant_size(p_quadrant->layer)); + Vector2 quadrant_pos = map_to_local(p_quadrant->coords * get_effective_quadrant_size(p_quadrant->layer)); for (const Vector2i &E_cell : p_quadrant->cells) { const TileMapCell &c = get_cell(p_quadrant->layer, E_cell, true); @@ -1283,9 +1330,9 @@ void TileMap::_rendering_draw_quadrant_debug(TileMapQuadrant *p_quadrant) { 0.8); // Draw a placeholder tile. - Transform2D xform; - xform.set_origin(map_to_world(E_cell) - quadrant_pos); - rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, xform); + Transform2D cell_to_quadrant; + cell_to_quadrant.set_origin(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); } } @@ -1398,15 +1445,15 @@ void TileMap::_physics_notification(int p_what) { #endif if (is_inside_tree() && (!collision_animatable || in_editor)) { // Update the new transform directly if we are not in animatable mode. - Transform2D global_transform = get_global_transform(); + Transform2D gl_transform = get_global_transform(); for (int layer = 0; layer < (int)layers.size(); layer++) { for (KeyValue<Vector2i, TileMapQuadrant> &E : layers[layer].quadrant_map) { TileMapQuadrant &q = E.value; for (RID body : q.bodies) { Transform2D xform; - xform.set_origin(map_to_world(bodies_coords[body])); - xform = global_transform * 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); } } @@ -1428,7 +1475,7 @@ void TileMap::_physics_notification(int p_what) { for (RID body : q.bodies) { Transform2D xform; - xform.set_origin(map_to_world(bodies_coords[body])); + 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); @@ -1449,9 +1496,9 @@ void TileMap::_physics_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r ERR_FAIL_COND(!is_inside_tree()); ERR_FAIL_COND(!tile_set.is_valid()); - Transform2D global_transform = get_global_transform(); - last_valid_transform = global_transform; - new_transform = global_transform; + Transform2D gl_transform = get_global_transform(); + last_valid_transform = gl_transform; + new_transform = gl_transform; PhysicsServer2D *ps = PhysicsServer2D::get_singleton(); RID space = get_world_2d()->get_space(); @@ -1498,8 +1545,8 @@ void TileMap::_physics_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r ps->body_set_space(body, space); Transform2D xform; - xform.set_origin(map_to_world(E_cell)); - xform = global_transform * xform; + xform.set_origin(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()); @@ -1584,14 +1631,14 @@ void TileMap::_physics_draw_quadrant_debug(TileMapQuadrant *p_quadrant) { Vector<Color> color; color.push_back(debug_collision_color); - Vector2 quadrant_pos = map_to_world(p_quadrant->coords * get_effective_quadrant_size(p_quadrant->layer)); - Transform2D qudrant_xform; - qudrant_xform.set_origin(quadrant_pos); - Transform2D global_transform_inv = (get_global_transform() * qudrant_xform).affine_inverse(); + Vector2 quadrant_pos = map_to_local(p_quadrant->coords * get_effective_quadrant_size(p_quadrant->layer)); + Transform2D quadrant_to_local; + quadrant_to_local.set_origin(quadrant_pos); + Transform2D global_to_quadrant = (get_global_transform() * quadrant_to_local).affine_inverse(); for (RID body : p_quadrant->bodies) { - Transform2D xform = Transform2D(ps->body_get_state(body, PhysicsServer2D::BODY_STATE_TRANSFORM)) * global_transform_inv; - rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, xform); + Transform2D body_to_quadrant = global_to_quadrant * Transform2D(ps->body_get_state(body, PhysicsServer2D::BODY_STATE_TRANSFORM)); + rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, body_to_quadrant); for (int shape_index = 0; shape_index < ps->body_get_shape_count(body); shape_index++) { const RID &shape = ps->body_get_shape(body, shape_index); PhysicsServer2D::ShapeType type = ps->shape_get_type(shape); @@ -1623,7 +1670,7 @@ void TileMap::_navigation_notification(int p_what) { continue; } Transform2D tile_transform; - tile_transform.set_origin(map_to_world(E_region.key)); + tile_transform.set_origin(map_to_local(E_region.key)); NavigationServer2D::get_singleton()->region_set_transform(region, tilemap_xform * tile_transform); } } @@ -1638,14 +1685,6 @@ void TileMap::_navigation_update_dirty_quadrants(SelfList<TileMapQuadrant>::List ERR_FAIL_COND(!is_inside_tree()); ERR_FAIL_COND(!tile_set.is_valid()); - // Get colors for debug. - SceneTree *st = SceneTree::get_singleton(); - Color debug_navigation_color; - bool debug_navigation = st && st->is_debugging_navigation_hint(); - if (debug_navigation) { - debug_navigation_color = st->get_debug_navigation_color(); - } - Transform2D tilemap_xform = get_global_transform(); SelfList<TileMapQuadrant> *q_list_element = r_dirty_quadrant_list.first(); while (q_list_element) { @@ -1691,7 +1730,7 @@ void TileMap::_navigation_update_dirty_quadrants(SelfList<TileMapQuadrant>::List if (navpoly.is_valid()) { Transform2D tile_transform; - tile_transform.set_origin(map_to_world(E_cell)); + tile_transform.set_origin(map_to_local(E_cell)); RID region = NavigationServer2D::get_singleton()->region_create(); NavigationServer2D::get_singleton()->region_set_map(region, get_world_2d()->get_navigation_map()); @@ -1748,10 +1787,13 @@ void TileMap::_navigation_draw_quadrant_debug(TileMapQuadrant *p_quadrant) { RenderingServer *rs = RenderingServer::get_singleton(); - Color color = get_tree()->get_debug_navigation_color(); + Color color = Color(0.5, 1.0, 1.0, 1.0); +#ifdef DEBUG_ENABLED + color = NavigationServer3D::get_singleton()->get_debug_navigation_geometry_face_color(); +#endif // DEBUG_ENABLED RandomPCG rand; - Vector2 quadrant_pos = map_to_world(p_quadrant->coords * get_effective_quadrant_size(p_quadrant->layer)); + Vector2 quadrant_pos = map_to_local(p_quadrant->coords * get_effective_quadrant_size(p_quadrant->layer)); for (const Vector2i &E_cell : p_quadrant->cells) { TileMapCell c = get_cell(p_quadrant->layer, E_cell, true); @@ -1773,9 +1815,9 @@ void TileMap::_navigation_draw_quadrant_debug(TileMapQuadrant *p_quadrant) { tile_data = atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile); } - Transform2D xform; - xform.set_origin(map_to_world(E_cell) - quadrant_pos); - rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, xform); + Transform2D cell_to_quadrant; + cell_to_quadrant.set_origin(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++) { Ref<NavigationPolygon> navpoly = tile_data->get_navigation_polygon(layer_index); @@ -1821,7 +1863,7 @@ void TileMap::_scenes_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_ for (const KeyValue<Vector2i, String> &E : q.scenes) { Node *node = get_node_or_null(E.value); if (node) { - node->queue_delete(); + node->queue_free(); } } @@ -1844,16 +1886,16 @@ void TileMap::_scenes_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_ Ref<PackedScene> packed_scene = scenes_collection_source->get_scene_tile_scene(c.alternative_tile); if (packed_scene.is_valid()) { Node *scene = packed_scene->instantiate(); - add_child(scene); 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_world(E_cell) + scene_as_control->get_position()); + scene_as_control->set_position(map_to_local(E_cell) + scene_as_control->get_position()); } else if (scene_as_node2d) { Transform2D xform; - xform.set_origin(map_to_world(E_cell)); + xform.set_origin(map_to_local(E_cell)); scene_as_node2d->set_transform(xform * scene_as_node2d->get_transform()); } + add_child(scene); q.scenes[E_cell] = scene->get_name(); } } @@ -1869,7 +1911,7 @@ void TileMap::_scenes_cleanup_quadrant(TileMapQuadrant *p_quadrant) { for (const KeyValue<Vector2i, String> &E : p_quadrant->scenes) { Node *node = get_node_or_null(E.value); if (node) { - node->queue_delete(); + node->queue_free(); } } @@ -1885,7 +1927,7 @@ 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_world(p_quadrant->coords * get_effective_quadrant_size(p_quadrant->layer)); + Vector2 quadrant_pos = map_to_local(p_quadrant->coords * get_effective_quadrant_size(p_quadrant->layer)); for (const Vector2i &E_cell : p_quadrant->cells) { const TileMapCell &c = get_cell(p_quadrant->layer, E_cell, true); @@ -1914,9 +1956,9 @@ void TileMap::_scenes_draw_quadrant_debug(TileMapQuadrant *p_quadrant) { 0.8); // Draw a placeholder tile. - Transform2D xform; - xform.set_origin(map_to_world(E_cell) - quadrant_pos); - rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, xform); + Transform2D cell_to_quadrant; + cell_to_quadrant.set_origin(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); } } @@ -2063,6 +2105,18 @@ int TileMap::get_cell_alternative_tile(int p_layer, const Vector2i &p_coords, bo 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); + ERR_FAIL_COND_V_MSG(source_id == TileSet::INVALID_SOURCE, nullptr, vformat("Invalid TileSetSource at cell %s. Make sure a tile exists at this cell.", p_coords)); + + 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)); + } + + return nullptr; +} + 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); @@ -2152,11 +2206,10 @@ void TileMap::set_pattern(int p_layer, Vector2i p_position, const Ref<TileMapPat } } -TileSet::TerrainsPattern TileMap::_get_best_terrain_pattern_for_constraints(int p_terrain_set, const Vector2i &p_position, RBSet<TerrainConstraint> p_constraints) { +TileSet::TerrainsPattern TileMap::_get_best_terrain_pattern_for_constraints(int p_terrain_set, const Vector2i &p_position, RBSet<TerrainConstraint> p_constraints, TileSet::TerrainsPattern p_current_pattern) { if (!tile_set.is_valid()) { return TileSet::TerrainsPattern(); } - // Returns all tiles compatible with the given constraints. RBMap<TileSet::TerrainsPattern, int> terrain_pattern_score; RBSet<TileSet::TerrainsPattern> pattern_set = tile_set->get_terrains_pattern_set(p_terrain_set); @@ -2167,28 +2220,41 @@ TileSet::TerrainsPattern TileMap::_get_best_terrain_pattern_for_constraints(int // Check the center bit constraint TerrainConstraint terrain_constraint = TerrainConstraint(this, p_position, terrain_pattern.get_terrain()); RBSet<TerrainConstraint>::Element *in_set_constraint_element = p_constraints.find(terrain_constraint); - if (in_set_constraint_element && in_set_constraint_element->get().get_terrain() != terrain_constraint.get_terrain()) { - score += in_set_constraint_element->get().get_priority(); + if (in_set_constraint_element) { + if (in_set_constraint_element->get().get_terrain() != terrain_constraint.get_terrain()) { + score += in_set_constraint_element->get().get_priority(); + } + } else if (p_current_pattern.get_terrain() != terrain_pattern.get_terrain()) { + continue; // Ignore a pattern that cannot keep bits without constraints unmodified. } // Check the surrounding bits + bool invalid_pattern = false; for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { 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)); in_set_constraint_element = p_constraints.find(terrain_bit_constraint); - if (in_set_constraint_element && in_set_constraint_element->get().get_terrain() != terrain_bit_constraint.get_terrain()) { - score += in_set_constraint_element->get().get_priority(); + if (in_set_constraint_element) { + if (in_set_constraint_element->get().get_terrain() != terrain_bit_constraint.get_terrain()) { + score += in_set_constraint_element->get().get_priority(); + } + } else if (p_current_pattern.get_terrain_peering_bit(bit) != terrain_pattern.get_terrain_peering_bit(bit)) { + invalid_pattern = true; // Ignore a pattern that cannot keep bits without constraints unmodified. + break; } } } + if (invalid_pattern) { + continue; + } terrain_pattern_score[terrain_pattern] = score; } // Compute the minimum score - TileSet::TerrainsPattern min_score_pattern; + TileSet::TerrainsPattern min_score_pattern = p_current_pattern; int min_score = INT32_MAX; for (KeyValue<TileSet::TerrainsPattern, int> E : terrain_pattern_score) { if (E.value < min_score) { @@ -2220,7 +2286,7 @@ RBSet<TileMap::TerrainConstraint> TileMap::_get_terrain_constraints_from_added_p return output; } -RBSet<TileMap::TerrainConstraint> TileMap::_get_terrain_constraints_from_cells_list(int p_layer, const RBSet<Vector2i> &p_cell_list, int p_terrain_set, bool p_ignore_empty_terrains) const { +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 { if (!tile_set.is_valid()) { return RBSet<TerrainConstraint>(); } @@ -2230,8 +2296,8 @@ RBSet<TileMap::TerrainConstraint> TileMap::_get_terrain_constraints_from_cells_l // Build a set of dummy constraints to get the constrained points. RBSet<TerrainConstraint> dummy_constraints; - for (const Vector2i &E : p_cell_list) { - for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { // Iterates over sides. + for (const Vector2i &E : p_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)); @@ -2288,7 +2354,7 @@ RBSet<TileMap::TerrainConstraint> TileMap::_get_terrain_constraints_from_cells_l } // Add the centers as constraints - for (Vector2i E_coords : p_cell_list) { + for (Vector2i E_coords : p_painted) { TileData *tile_data = nullptr; TileMapCell cell = get_cell(p_layer, E_coords); if (cell.source_id != TileSet::INVALID_SOURCE) { @@ -2308,7 +2374,7 @@ RBSet<TileMap::TerrainConstraint> TileMap::_get_terrain_constraints_from_cells_l return constraints; } -HashMap<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_fill_constraints(const Vector<Vector2i> &p_to_replace, int p_terrain_set, const RBSet<TerrainConstraint> p_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) { if (!tile_set.is_valid()) { return HashMap<Vector2i, TileSet::TerrainsPattern>(); } @@ -2324,7 +2390,20 @@ HashMap<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_fill_constraints(co const Vector2i &coords = p_to_replace[i]; // Select the best pattern for the given constraints - TileSet::TerrainsPattern pattern = _get_best_terrain_pattern_for_constraints(p_terrain_set, coords, constraints); + TileSet::TerrainsPattern current_pattern = TileSet::TerrainsPattern(*tile_set, p_terrain_set); + TileMapCell cell = get_cell(p_layer, 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); + if (atlas_source) { + // Get tile data. + TileData *tile_data = atlas_source->get_tile_data(cell.get_atlas_coords(), cell.alternative_tile); + if (tile_data && tile_data->get_terrain_set() == p_terrain_set) { + current_pattern = tile_data->get_terrains_pattern(); + } + } + } + TileSet::TerrainsPattern pattern = _get_best_terrain_pattern_for_constraints(p_terrain_set, coords, constraints, current_pattern); // Update the constraint set with the new ones RBSet<TerrainConstraint> new_constraints = _get_terrain_constraints_from_added_pattern(coords, p_terrain_set, pattern); @@ -2361,7 +2440,7 @@ HashMap<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_fill_connect(int p_ // 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)) { + if (is_existing_neighbor(bit)) { Vector2i neighbor = get_neighbor_cell(coords, bit); if (!can_modify_set.has(neighbor)) { can_modify_list.push_back(neighbor); @@ -2438,12 +2517,12 @@ 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_cells_list(p_layer, can_modify_set, p_terrain_set, p_ignore_empty_terrains)) { + for (TerrainConstraint c : _get_terrain_constraints_from_painted_cells_list(p_layer, painted_set, p_terrain_set, p_ignore_empty_terrains)) { constraints.insert(c); } // Fill the terrains. - output = terrain_fill_constraints(can_modify_list, p_terrain_set, constraints); + output = terrain_fill_constraints(p_layer, can_modify_list, p_terrain_set, constraints); return output; } @@ -2459,7 +2538,7 @@ HashMap<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_fill_path(int p_lay TileSet::CellNeighbor found_bit = TileSet::CELL_NEIGHBOR_MAX; for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) { TileSet::CellNeighbor bit = TileSet::CellNeighbor(j); - if (tile_set->is_valid_terrain_peering_bit(p_terrain_set, bit)) { + if (is_existing_neighbor(bit)) { if (get_neighbor_cell(p_path[i], bit) == p_path[i + 1]) { found_bit = bit; break; @@ -2473,10 +2552,12 @@ HashMap<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_fill_path(int p_lay // 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]; can_modify_list.push_back(coords); can_modify_set.insert(coords); + painted_set.insert(coords); } for (Vector2i coords : p_path) { // Find the adequate neighbor @@ -2509,12 +2590,12 @@ HashMap<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_fill_path(int p_lay } // Fills in the constraint list from existing tiles. - for (TerrainConstraint c : _get_terrain_constraints_from_cells_list(p_layer, can_modify_set, p_terrain_set, p_ignore_empty_terrains)) { + for (TerrainConstraint c : _get_terrain_constraints_from_painted_cells_list(p_layer, painted_set, p_terrain_set, p_ignore_empty_terrains)) { constraints.insert(c); } // Fill the terrains. - output = terrain_fill_constraints(can_modify_list, p_terrain_set, constraints); + output = terrain_fill_constraints(p_layer, can_modify_list, p_terrain_set, constraints); return output; } @@ -2526,10 +2607,12 @@ HashMap<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_fill_pattern(int p_ // 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_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_coords_array) { // Find the adequate neighbor @@ -2559,12 +2642,12 @@ 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_cells_list(p_layer, can_modify_set, p_terrain_set, p_ignore_empty_terrains)) { + for (TerrainConstraint c : _get_terrain_constraints_from_painted_cells_list(p_layer, painted_set, p_terrain_set, p_ignore_empty_terrains)) { constraints.insert(c); } // Fill the terrains. - output = terrain_fill_constraints(can_modify_list, p_terrain_set, constraints); + output = terrain_fill_constraints(p_layer, can_modify_list, p_terrain_set, constraints); return output; } @@ -2573,14 +2656,38 @@ void TileMap::set_cells_terrain_connect(int p_layer, TypedArray<Vector2i> p_cell ERR_FAIL_INDEX(p_layer, (int)layers.size()); ERR_FAIL_INDEX(p_terrain_set, tile_set->get_terrain_sets_count()); - Vector<Vector2i> vector_cells; + Vector<Vector2i> cells_vector; + HashSet<Vector2i> painted_set; for (int i = 0; i < p_cells.size(); i++) { - vector_cells.push_back(p_cells[i]); - } - HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_output = terrain_fill_connect(p_layer, vector_cells, p_terrain_set, p_terrain, p_ignore_empty_terrains); - for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &E : terrain_fill_output) { - TileMapCell c = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, E.value); - set_cell(p_layer, E.key, c.source_id, c.get_atlas_coords(), c.alternative_tile); + 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); + 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); + } 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); + if (cell.source_id != TileSet::INVALID_SOURCE) { + TileSetSource *source = *tile_set->get_source(cell.source_id); + TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); + if (atlas_source) { + // Get tile data. + TileData *tile_data = atlas_source->get_tile_data(cell.get_atlas_coords(), cell.alternative_tile); + if (tile_data && tile_data->get_terrain_set() == p_terrain_set) { + in_map_terrain_pattern = tile_data->get_terrains_pattern(); + } + } + } + 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); + } + } } } @@ -2590,13 +2697,38 @@ void TileMap::set_cells_terrain_path(int p_layer, TypedArray<Vector2i> p_path, i ERR_FAIL_INDEX(p_terrain_set, tile_set->get_terrain_sets_count()); Vector<Vector2i> vector_path; + HashSet<Vector2i> painted_set; for (int i = 0; i < p_path.size(); i++) { vector_path.push_back(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); - for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &E : terrain_fill_output) { - TileMapCell c = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, E.value); - set_cell(p_layer, E.key, c.source_id, c.get_atlas_coords(), c.alternative_tile); + 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); + } 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); + if (cell.source_id != TileSet::INVALID_SOURCE) { + TileSetSource *source = *tile_set->get_source(cell.source_id); + TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); + if (atlas_source) { + // Get tile data. + TileData *tile_data = atlas_source->get_tile_data(cell.get_atlas_coords(), cell.alternative_tile); + if (tile_data && tile_data->get_terrain_set() == p_terrain_set) { + in_map_terrain_pattern = tile_data->get_terrains_pattern(); + } + } + } + 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); + } + } } } @@ -2765,9 +2897,9 @@ Vector<int> TileMap::_get_tile_data(int p_layer) const { // Export tile data to raw format const HashMap<Vector2i, TileMapCell> &tile_map = layers[p_layer].tile_map; - Vector<int> data; - data.resize(tile_map.size() * 3); - int *w = data.ptrw(); + Vector<int> tile_data; + tile_data.resize(tile_map.size() * 3); + int *w = tile_data.ptrw(); // Save in highest format @@ -2783,7 +2915,7 @@ Vector<int> TileMap::_get_tile_data(int p_layer) const { idx += 3; } - return data; + return tile_data; } void TileMap::_build_runtime_update_tile_data(SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) { @@ -2792,7 +2924,7 @@ void TileMap::_build_runtime_update_tile_data(SelfList<TileMapQuadrant>::List &r while (q_list_element) { TileMapQuadrant &q = *q_list_element->self(); // Iterate over the cells of the quadrant. - for (const KeyValue<Vector2i, Vector2i> &E_cell : q.world_to_map) { + for (const KeyValue<Vector2i, Vector2i> &E_cell : q.local_to_map) { TileMapCell c = get_cell(q.layer, E_cell.value, true); TileSetSource *source; @@ -2951,7 +3083,7 @@ void TileMap::_get_property_list(List<PropertyInfo> *p_list) const { } } -Vector2 TileMap::map_to_world(const Vector2i &p_pos) const { +Vector2 TileMap::map_to_local(const Vector2i &p_pos) const { // SHOULD RETURN THE CENTER OF THE CELL ERR_FAIL_COND_V(!tile_set.is_valid(), Vector2()); @@ -3028,10 +3160,10 @@ Vector2 TileMap::map_to_world(const Vector2i &p_pos) const { return (ret + Vector2(0.5, 0.5)) * tile_set->get_tile_size(); } -Vector2i TileMap::world_to_map(const Vector2 &p_pos) const { +Vector2i TileMap::local_to_map(const Vector2 &p_local_position) const { ERR_FAIL_COND_V(!tile_set.is_valid(), Vector2i()); - Vector2 ret = p_pos; + Vector2 ret = p_local_position; ret /= tile_set->get_tile_size(); TileSet::TileShape tile_shape = tile_set->get_tile_shape(); @@ -3056,7 +3188,7 @@ Vector2i TileMap::world_to_map(const Vector2 &p_pos) const { ret.x /= overlapping_ratio; } - // For each half-offset shape, we check if we are in the corner of the tile, and thus should correct the world position accordingly. + // 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 @@ -3586,7 +3718,7 @@ TypedArray<Vector2i> TileMap::get_used_cells(int p_layer) const { return a; } -Rect2 TileMap::get_used_rect() { // Not const because of cache +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; @@ -3663,13 +3795,14 @@ void TileMap::set_use_parent_material(bool p_use_parent_material) { } void TileMap::set_texture_filter(TextureFilter p_texture_filter) { - // Set a default texture filter for the whole tilemap + // 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(p_texture_filter)); + RenderingServer::get_singleton()->canvas_item_set_default_texture_filter(ci, RS::CanvasItemTextureFilter(target_filter)); _make_quadrant_dirty(F); } } @@ -3678,13 +3811,14 @@ void TileMap::set_texture_filter(TextureFilter p_texture_filter) { } void TileMap::set_texture_repeat(CanvasItem::TextureRepeat p_texture_repeat) { - // Set a default texture repeat for the whole tilemap + // 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(p_texture_repeat)); + RenderingServer::get_singleton()->canvas_item_set_default_texture_repeat(ci, RS::CanvasItemTextureRepeat(target_repeat)); _make_quadrant_dirty(F); } } @@ -3741,7 +3875,7 @@ void TileMap::draw_cells_outline(Control *p_control, RBSet<Vector2i> p_cells, Co TileSet::TileShape shape = tile_set->get_tile_shape(); for (const Vector2i &E : p_cells) { - Vector2 center = map_to_world(E); + Vector2 center = map_to_local(E); #define DRAW_SIDE_IF_NEEDED(side, polygon_index_from, polygon_index_to) \ if (!p_cells.has(get_neighbor_cell(E, side))) { \ @@ -3790,8 +3924,8 @@ void TileMap::draw_cells_outline(Control *p_control, RBSet<Vector2i> p_cells, Co #undef DRAW_SIDE_IF_NEEDED } -TypedArray<String> TileMap::get_configuration_warnings() const { - TypedArray<String> warnings = Node::get_configuration_warnings(); +PackedStringArray TileMap::get_configuration_warnings() const { + PackedStringArray warnings = Node::get_configuration_warnings(); // Retrieve the set of Z index values with a Y-sorted layer. RBSet<int> y_sorted_z_index; @@ -3827,7 +3961,7 @@ void TileMap::_bind_methods() { ClassDB::bind_method(D_METHOD("get_layer_name", "layer"), &TileMap::get_layer_name); ClassDB::bind_method(D_METHOD("set_layer_enabled", "layer", "enabled"), &TileMap::set_layer_enabled); ClassDB::bind_method(D_METHOD("is_layer_enabled", "layer"), &TileMap::is_layer_enabled); - ClassDB::bind_method(D_METHOD("set_layer_modulate", "layer", "enabled"), &TileMap::set_layer_modulate); + ClassDB::bind_method(D_METHOD("set_layer_modulate", "layer", "modulate"), &TileMap::set_layer_modulate); ClassDB::bind_method(D_METHOD("get_layer_modulate", "layer"), &TileMap::get_layer_modulate); ClassDB::bind_method(D_METHOD("set_layer_y_sort_enabled", "layer", "y_sort_enabled"), &TileMap::set_layer_y_sort_enabled); ClassDB::bind_method(D_METHOD("is_layer_y_sort_enabled", "layer"), &TileMap::is_layer_y_sort_enabled); @@ -3846,9 +3980,10 @@ void TileMap::_bind_methods() { 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); - ClassDB::bind_method(D_METHOD("get_cell_atlas_coords", "layer", "coords", "use_proxies"), &TileMap::get_cell_atlas_coords); - ClassDB::bind_method(D_METHOD("get_cell_alternative_tile", "layer", "coords", "use_proxies"), &TileMap::get_cell_alternative_tile); + ClassDB::bind_method(D_METHOD("get_cell_source_id", "layer", "coords", "use_proxies"), &TileMap::get_cell_source_id, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("get_cell_atlas_coords", "layer", "coords", "use_proxies"), &TileMap::get_cell_atlas_coords, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("get_cell_alternative_tile", "layer", "coords", "use_proxies"), &TileMap::get_cell_alternative_tile, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("get_cell_tile_data", "layer", "coords", "use_proxies"), &TileMap::get_cell_tile_data, DEFVAL(false)); ClassDB::bind_method(D_METHOD("get_coords_for_body_rid", "body"), &TileMap::get_coords_for_body_rid); @@ -3870,8 +4005,8 @@ void TileMap::_bind_methods() { ClassDB::bind_method(D_METHOD("get_used_cells", "layer"), &TileMap::get_used_cells); ClassDB::bind_method(D_METHOD("get_used_rect"), &TileMap::get_used_rect); - ClassDB::bind_method(D_METHOD("map_to_world", "map_position"), &TileMap::map_to_world); - ClassDB::bind_method(D_METHOD("world_to_map", "world_position"), &TileMap::world_to_map); + ClassDB::bind_method(D_METHOD("map_to_local", "map_position"), &TileMap::map_to_local); + ClassDB::bind_method(D_METHOD("local_to_map", "local_position"), &TileMap::local_to_map); ClassDB::bind_method(D_METHOD("get_neighbor_cell", "coords", "neighbor"), &TileMap::get_neighbor_cell); @@ -3921,9 +4056,5 @@ TileMap::TileMap() { } TileMap::~TileMap() { - if (tile_set.is_valid()) { - tile_set->disconnect("changed", callable_mp(this, &TileMap::_tile_set_changed)); - } - _clear_internals(); } |