diff options
Diffstat (limited to 'scene/resources/tile_set.cpp')
-rw-r--r-- | scene/resources/tile_set.cpp | 1440 |
1 files changed, 1070 insertions, 370 deletions
diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp index fcd31143a8..f5e52b70e6 100644 --- a/scene/resources/tile_set.cpp +++ b/scene/resources/tile_set.cpp @@ -66,12 +66,13 @@ const char *TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[] = { void TileSet::set_tile_shape(TileSet::TileShape p_shape) { tile_shape = p_shape; - for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) { - E_source->get()->notify_tile_data_properties_should_change(); + for (KeyValue<int, Ref<TileSetSource>> &E_source : sources) { + E_source.value->notify_tile_data_properties_should_change(); } terrain_bits_meshes_dirty = true; tile_meshes_dirty = true; + notify_property_list_changed(); emit_changed(); } TileSet::TileShape TileSet::get_tile_shape() const { @@ -89,8 +90,8 @@ TileSet::TileLayout TileSet::get_tile_layout() const { void TileSet::set_tile_offset_axis(TileSet::TileOffsetAxis p_alignment) { tile_offset_axis = p_alignment; - for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) { - E_source->get()->notify_tile_data_properties_should_change(); + for (KeyValue<int, Ref<TileSetSource>> &E_source : sources) { + E_source.value->notify_tile_data_properties_should_change(); } terrain_bits_meshes_dirty = true; @@ -170,6 +171,8 @@ void TileSet::set_source_id(int p_source_id, int p_new_source_id) { source_ids.append(p_new_source_id); source_ids.sort(); + _compute_next_source_id(); + emit_changed(); } @@ -205,25 +208,46 @@ bool TileSet::is_uv_clipping() const { return uv_clipping; }; -void TileSet::set_occlusion_layers_count(int p_occlusion_layers_count) { - ERR_FAIL_COND(p_occlusion_layers_count < 0); - if (occlusion_layers.size() == p_occlusion_layers_count) { - return; - } +int TileSet::get_occlusion_layers_count() const { + return occlusion_layers.size(); +}; - occlusion_layers.resize(p_occlusion_layers_count); +void TileSet::add_occlusion_layer(int p_index) { + if (p_index < 0) { + p_index = occlusion_layers.size(); + } + ERR_FAIL_INDEX(p_index, occlusion_layers.size() + 1); + occlusion_layers.insert(p_index, OcclusionLayer()); - for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) { - E_source->get()->notify_tile_data_properties_should_change(); + for (KeyValue<int, Ref<TileSetSource>> source : sources) { + source.value->add_occlusion_layer(p_index); } notify_property_list_changed(); emit_changed(); } -int TileSet::get_occlusion_layers_count() const { - return occlusion_layers.size(); -}; +void TileSet::move_occlusion_layer(int p_from_index, int p_to_pos) { + ERR_FAIL_INDEX(p_from_index, occlusion_layers.size()); + ERR_FAIL_INDEX(p_to_pos, occlusion_layers.size() + 1); + occlusion_layers.insert(p_to_pos, occlusion_layers[p_from_index]); + occlusion_layers.remove(p_to_pos < p_from_index ? p_from_index + 1 : p_from_index); + for (KeyValue<int, Ref<TileSetSource>> source : sources) { + source.value->move_occlusion_layer(p_from_index, p_to_pos); + } + notify_property_list_changed(); + emit_changed(); +} + +void TileSet::remove_occlusion_layer(int p_index) { + ERR_FAIL_INDEX(p_index, occlusion_layers.size()); + occlusion_layers.remove(p_index); + for (KeyValue<int, Ref<TileSetSource>> source : sources) { + source.value->remove_occlusion_layer(p_index); + } + notify_property_list_changed(); + emit_changed(); +} void TileSet::set_occlusion_layer_light_mask(int p_layer_index, int p_light_mask) { ERR_FAIL_INDEX(p_layer_index, occlusion_layers.size()); @@ -236,7 +260,7 @@ int TileSet::get_occlusion_layer_light_mask(int p_layer_index) const { return occlusion_layers[p_layer_index].light_mask; } -void TileSet::set_occlusion_layer_sdf_collision(int p_layer_index, int p_sdf_collision) { +void TileSet::set_occlusion_layer_sdf_collision(int p_layer_index, bool p_sdf_collision) { ERR_FAIL_INDEX(p_layer_index, occlusion_layers.size()); occlusion_layers.write[p_layer_index].sdf_collision = p_sdf_collision; emit_changed(); @@ -247,25 +271,45 @@ bool TileSet::get_occlusion_layer_sdf_collision(int p_layer_index) const { return occlusion_layers[p_layer_index].sdf_collision; } -// Physics -void TileSet::set_physics_layers_count(int p_physics_layers_count) { - ERR_FAIL_COND(p_physics_layers_count < 0); - if (physics_layers.size() == p_physics_layers_count) { - return; - } +int TileSet::get_physics_layers_count() const { + return physics_layers.size(); +} - physics_layers.resize(p_physics_layers_count); +void TileSet::add_physics_layer(int p_index) { + if (p_index < 0) { + p_index = physics_layers.size(); + } + ERR_FAIL_INDEX(p_index, physics_layers.size() + 1); + physics_layers.insert(p_index, PhysicsLayer()); - for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) { - E_source->get()->notify_tile_data_properties_should_change(); + for (KeyValue<int, Ref<TileSetSource>> source : sources) { + source.value->add_physics_layer(p_index); } notify_property_list_changed(); emit_changed(); } -int TileSet::get_physics_layers_count() const { - return physics_layers.size(); +void TileSet::move_physics_layer(int p_from_index, int p_to_pos) { + ERR_FAIL_INDEX(p_from_index, physics_layers.size()); + ERR_FAIL_INDEX(p_to_pos, physics_layers.size() + 1); + physics_layers.insert(p_to_pos, physics_layers[p_from_index]); + physics_layers.remove(p_to_pos < p_from_index ? p_from_index + 1 : p_from_index); + for (KeyValue<int, Ref<TileSetSource>> source : sources) { + source.value->move_physics_layer(p_from_index, p_to_pos); + } + notify_property_list_changed(); + emit_changed(); +} + +void TileSet::remove_physics_layer(int p_index) { + ERR_FAIL_INDEX(p_index, physics_layers.size()); + physics_layers.remove(p_index); + for (KeyValue<int, Ref<TileSetSource>> source : sources) { + source.value->remove_physics_layer(p_index); + } + notify_property_list_changed(); + emit_changed(); } void TileSet::set_physics_layer_collision_layer(int p_layer_index, uint32_t p_layer) { @@ -301,24 +345,52 @@ Ref<PhysicsMaterial> TileSet::get_physics_layer_physics_material(int p_layer_ind } // Terrains -void TileSet::set_terrain_sets_count(int p_terrains_sets_count) { - ERR_FAIL_COND(p_terrains_sets_count < 0); +int TileSet::get_terrain_sets_count() const { + return terrain_sets.size(); +} - terrain_sets.resize(p_terrains_sets_count); +void TileSet::add_terrain_set(int p_index) { + if (p_index < 0) { + p_index = terrain_sets.size(); + } + ERR_FAIL_INDEX(p_index, terrain_sets.size() + 1); + terrain_sets.insert(p_index, TerrainSet()); + + for (KeyValue<int, Ref<TileSetSource>> source : sources) { + source.value->add_terrain_set(p_index); + } notify_property_list_changed(); emit_changed(); } -int TileSet::get_terrain_sets_count() const { - return terrain_sets.size(); +void TileSet::move_terrain_set(int p_from_index, int p_to_pos) { + ERR_FAIL_INDEX(p_from_index, terrain_sets.size()); + ERR_FAIL_INDEX(p_to_pos, terrain_sets.size() + 1); + terrain_sets.insert(p_to_pos, terrain_sets[p_from_index]); + terrain_sets.remove(p_to_pos < p_from_index ? p_from_index + 1 : p_from_index); + for (KeyValue<int, Ref<TileSetSource>> source : sources) { + source.value->move_terrain_set(p_from_index, p_to_pos); + } + notify_property_list_changed(); + emit_changed(); +} + +void TileSet::remove_terrain_set(int p_index) { + ERR_FAIL_INDEX(p_index, terrain_sets.size()); + terrain_sets.remove(p_index); + for (KeyValue<int, Ref<TileSetSource>> source : sources) { + source.value->remove_terrain_set(p_index); + } + notify_property_list_changed(); + emit_changed(); } void TileSet::set_terrain_set_mode(int p_terrain_set, TerrainMode p_terrain_mode) { ERR_FAIL_INDEX(p_terrain_set, terrain_sets.size()); terrain_sets.write[p_terrain_set].mode = p_terrain_mode; - for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) { - E_source->get()->notify_tile_data_properties_should_change(); + for (KeyValue<int, Ref<TileSetSource>> &E_source : sources) { + E_source.value->notify_tile_data_properties_should_change(); } notify_property_list_changed(); @@ -330,36 +402,61 @@ TileSet::TerrainMode TileSet::get_terrain_set_mode(int p_terrain_set) const { return terrain_sets[p_terrain_set].mode; } -void TileSet::set_terrains_count(int p_terrain_set, int p_terrains_layers_count) { +int TileSet::get_terrains_count(int p_terrain_set) const { + ERR_FAIL_INDEX_V(p_terrain_set, terrain_sets.size(), -1); + return terrain_sets[p_terrain_set].terrains.size(); +} + +void TileSet::add_terrain(int p_terrain_set, int p_index) { ERR_FAIL_INDEX(p_terrain_set, terrain_sets.size()); - ERR_FAIL_COND(p_terrains_layers_count < 0); - if (terrain_sets[p_terrain_set].terrains.size() == p_terrains_layers_count) { - return; + Vector<Terrain> &terrains = terrain_sets.write[p_terrain_set].terrains; + if (p_index < 0) { + p_index = terrains.size(); } - - int old_size = terrain_sets[p_terrain_set].terrains.size(); - terrain_sets.write[p_terrain_set].terrains.resize(p_terrains_layers_count); + ERR_FAIL_INDEX(p_index, terrains.size() + 1); + terrains.insert(p_index, Terrain()); // Default name and color - for (int i = old_size; i < terrain_sets.write[p_terrain_set].terrains.size(); i++) { - float hue_rotate = (i * 2 % 16) / 16.0; - Color c; - c.set_hsv(Math::fmod(float(hue_rotate), float(1.0)), 0.5, 0.5); - terrain_sets.write[p_terrain_set].terrains.write[i].color = c; - terrain_sets.write[p_terrain_set].terrains.write[i].name = String(vformat("Terrain %d", i)); + float hue_rotate = (terrains.size() % 16) / 16.0; + Color c; + c.set_hsv(Math::fmod(float(hue_rotate), float(1.0)), 0.5, 0.5); + terrains.write[p_index].color = c; + terrains.write[p_index].name = String(vformat("Terrain %d", p_index)); + + for (KeyValue<int, Ref<TileSetSource>> source : sources) { + source.value->add_terrain(p_terrain_set, p_index); } - for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) { - E_source->get()->notify_tile_data_properties_should_change(); - } + notify_property_list_changed(); + emit_changed(); +} +void TileSet::move_terrain(int p_terrain_set, int p_from_index, int p_to_pos) { + ERR_FAIL_INDEX(p_terrain_set, terrain_sets.size()); + Vector<Terrain> &terrains = terrain_sets.write[p_terrain_set].terrains; + + ERR_FAIL_INDEX(p_from_index, terrains.size()); + ERR_FAIL_INDEX(p_to_pos, terrains.size() + 1); + terrains.insert(p_to_pos, terrains[p_from_index]); + terrains.remove(p_to_pos < p_from_index ? p_from_index + 1 : p_from_index); + for (KeyValue<int, Ref<TileSetSource>> source : sources) { + source.value->move_terrain(p_terrain_set, p_from_index, p_to_pos); + } notify_property_list_changed(); emit_changed(); } -int TileSet::get_terrains_count(int p_terrain_set) const { - ERR_FAIL_INDEX_V(p_terrain_set, terrain_sets.size(), -1); - return terrain_sets[p_terrain_set].terrains.size(); +void TileSet::remove_terrain(int p_terrain_set, int p_index) { + ERR_FAIL_INDEX(p_terrain_set, terrain_sets.size()); + Vector<Terrain> &terrains = terrain_sets.write[p_terrain_set].terrains; + + ERR_FAIL_INDEX(p_index, terrains.size()); + terrains.remove(p_index); + for (KeyValue<int, Ref<TileSetSource>> source : sources) { + source.value->remove_terrain(p_terrain_set, p_index); + } + notify_property_list_changed(); + emit_changed(); } void TileSet::set_terrain_name(int p_terrain_set, int p_terrain_index, String p_name) { @@ -485,24 +582,45 @@ bool TileSet::is_valid_peering_bit_terrain(int p_terrain_set, TileSet::CellNeigh } // Navigation -void TileSet::set_navigation_layers_count(int p_navigation_layers_count) { - ERR_FAIL_COND(p_navigation_layers_count < 0); - if (navigation_layers.size() == p_navigation_layers_count) { - return; - } +int TileSet::get_navigation_layers_count() const { + return navigation_layers.size(); +} - navigation_layers.resize(p_navigation_layers_count); +void TileSet::add_navigation_layer(int p_index) { + if (p_index < 0) { + p_index = navigation_layers.size(); + } + ERR_FAIL_INDEX(p_index, navigation_layers.size() + 1); + navigation_layers.insert(p_index, NavigationLayer()); - for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) { - E_source->get()->notify_tile_data_properties_should_change(); + for (KeyValue<int, Ref<TileSetSource>> source : sources) { + source.value->add_navigation_layer(p_index); } notify_property_list_changed(); emit_changed(); } -int TileSet::get_navigation_layers_count() const { - return navigation_layers.size(); +void TileSet::move_navigation_layer(int p_from_index, int p_to_pos) { + ERR_FAIL_INDEX(p_from_index, navigation_layers.size()); + ERR_FAIL_INDEX(p_to_pos, navigation_layers.size() + 1); + navigation_layers.insert(p_to_pos, navigation_layers[p_from_index]); + navigation_layers.remove(p_to_pos < p_from_index ? p_from_index + 1 : p_from_index); + for (KeyValue<int, Ref<TileSetSource>> source : sources) { + source.value->move_navigation_layer(p_from_index, p_to_pos); + } + notify_property_list_changed(); + emit_changed(); +} + +void TileSet::remove_navigation_layer(int p_index) { + ERR_FAIL_INDEX(p_index, navigation_layers.size()); + navigation_layers.remove(p_index); + for (KeyValue<int, Ref<TileSetSource>> source : sources) { + source.value->remove_navigation_layer(p_index); + } + notify_property_list_changed(); + emit_changed(); } void TileSet::set_navigation_layer_layers(int p_layer_index, uint32_t p_layers) { @@ -517,30 +635,52 @@ uint32_t TileSet::get_navigation_layer_layers(int p_layer_index) const { } // Custom data. -void TileSet::set_custom_data_layers_count(int p_custom_data_layers_count) { - ERR_FAIL_COND(p_custom_data_layers_count < 0); - if (custom_data_layers.size() == p_custom_data_layers_count) { - return; - } - - custom_data_layers.resize(p_custom_data_layers_count); +int TileSet::get_custom_data_layers_count() const { + return custom_data_layers.size(); +} - for (Map<String, int>::Element *E = custom_data_layers_by_name.front(); E; E = E->next()) { - if (E->get() >= custom_data_layers.size()) { - custom_data_layers_by_name.erase(E); - } +void TileSet::add_custom_data_layer(int p_index) { + if (p_index < 0) { + p_index = custom_data_layers.size(); } + ERR_FAIL_INDEX(p_index, custom_data_layers.size() + 1); + custom_data_layers.insert(p_index, CustomDataLayer()); - for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) { - E_source->get()->notify_tile_data_properties_should_change(); + for (KeyValue<int, Ref<TileSetSource>> source : sources) { + source.value->add_custom_data_layer(p_index); } notify_property_list_changed(); emit_changed(); } -int TileSet::get_custom_data_layers_count() const { - return custom_data_layers.size(); +void TileSet::move_custom_data_layer(int p_from_index, int p_to_pos) { + ERR_FAIL_INDEX(p_from_index, custom_data_layers.size()); + ERR_FAIL_INDEX(p_to_pos, custom_data_layers.size() + 1); + custom_data_layers.insert(p_to_pos, custom_data_layers[p_from_index]); + custom_data_layers.remove(p_to_pos < p_from_index ? p_from_index + 1 : p_from_index); + for (KeyValue<int, Ref<TileSetSource>> source : sources) { + source.value->move_custom_data_layer(p_from_index, p_to_pos); + } + notify_property_list_changed(); + emit_changed(); +} + +void TileSet::remove_custom_data_layer(int p_index) { + ERR_FAIL_INDEX(p_index, custom_data_layers.size()); + custom_data_layers.remove(p_index); + for (KeyValue<String, int> E : custom_data_layers_by_name) { + if (E.value == p_index) { + custom_data_layers_by_name.erase(E.key); + break; + } + } + + for (KeyValue<int, Ref<TileSetSource>> source : sources) { + source.value->remove_custom_data_layer(p_index); + } + notify_property_list_changed(); + emit_changed(); } int TileSet::get_custom_data_layer_by_name(String p_value) const { @@ -582,8 +722,8 @@ void TileSet::set_custom_data_type(int p_layer_id, Variant::Type p_value) { ERR_FAIL_INDEX(p_layer_id, custom_data_layers.size()); custom_data_layers.write[p_layer_id].type = p_value; - for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) { - E_source->get()->notify_tile_data_properties_should_change(); + for (KeyValue<int, Ref<TileSetSource>> &E_source : sources) { + E_source.value->notify_tile_data_properties_should_change(); } emit_changed(); @@ -721,10 +861,10 @@ void TileSet::remove_alternative_level_tile_proxy(int p_source_from, Vector2i p_ Array TileSet::get_source_level_tile_proxies() const { Array output; - for (Map<int, int>::Element *E = source_level_proxies.front(); E; E = E->next()) { + for (const KeyValue<int, int> &E : source_level_proxies) { Array proxy; - proxy.push_back(E->key()); - proxy.push_back(E->get()); + proxy.push_back(E.key); + proxy.push_back(E.value); output.push_back(proxy); } return output; @@ -732,10 +872,10 @@ Array TileSet::get_source_level_tile_proxies() const { Array TileSet::get_coords_level_tile_proxies() const { Array output; - for (Map<Array, Array>::Element *E = coords_level_proxies.front(); E; E = E->next()) { + for (const KeyValue<Array, Array> &E : coords_level_proxies) { Array proxy; - proxy.append_array(E->key()); - proxy.append_array(E->get()); + proxy.append_array(E.key); + proxy.append_array(E.value); output.push_back(proxy); } return output; @@ -743,10 +883,10 @@ Array TileSet::get_coords_level_tile_proxies() const { Array TileSet::get_alternative_level_tile_proxies() const { Array output; - for (Map<Array, Array>::Element *E = alternative_level_proxies.front(); E; E = E->next()) { + for (const KeyValue<Array, Array> &E : alternative_level_proxies) { Array proxy; - proxy.append_array(E->key()); - proxy.append_array(E->get()); + proxy.append_array(E.key); + proxy.append_array(E.value); output.push_back(proxy); } return output; @@ -798,9 +938,9 @@ Array TileSet::map_tile_proxy(int p_source_from, Vector2i p_coords_from, int p_a void TileSet::cleanup_invalid_tile_proxies() { // Source level. Vector<int> source_to_remove; - for (Map<int, int>::Element *E = source_level_proxies.front(); E; E = E->next()) { - if (has_source(E->key())) { - source_to_remove.append(E->key()); + for (const KeyValue<int, int> &E : source_level_proxies) { + if (has_source(E.key)) { + source_to_remove.append(E.key); } } for (int i = 0; i < source_to_remove.size(); i++) { @@ -809,8 +949,8 @@ void TileSet::cleanup_invalid_tile_proxies() { // Coords level. Vector<Array> coords_to_remove; - for (Map<Array, Array>::Element *E = coords_level_proxies.front(); E; E = E->next()) { - Array a = E->key(); + for (const KeyValue<Array, Array> &E : coords_level_proxies) { + Array a = E.key; if (has_source(a[0]) && get_source(a[0])->has_tile(a[1])) { coords_to_remove.append(a); } @@ -822,8 +962,8 @@ void TileSet::cleanup_invalid_tile_proxies() { // Alternative level. Vector<Array> alternative_to_remove; - for (Map<Array, Array>::Element *E = alternative_level_proxies.front(); E; E = E->next()) { - Array a = E->key(); + for (const KeyValue<Array, Array> &E : alternative_level_proxies) { + Array a = E.key; if (has_source(a[0]) && get_source(a[0])->has_tile(a[1]) && get_source(a[0])->has_alternative_tile(a[1], a[2])) { alternative_to_remove.append(a); } @@ -845,10 +985,10 @@ void TileSet::clear_tile_proxies() { Vector<Vector2> TileSet::get_tile_shape_polygon() { Vector<Vector2> points; if (tile_shape == TileSet::TILE_SHAPE_SQUARE) { - points.append(Vector2(0.0, 0.0)); - points.append(Vector2(1.0, 0.0)); - points.append(Vector2(1.0, 1.0)); - points.append(Vector2(0.0, 1.0)); + points.append(Vector2(-0.5, -0.5)); + points.append(Vector2(0.5, -0.5)); + points.append(Vector2(0.5, 0.5)); + points.append(Vector2(-0.5, 0.5)); } else { float overlap = 0.0; switch (tile_shape) { @@ -865,44 +1005,42 @@ Vector<Vector2> TileSet::get_tile_shape_polygon() { break; } - points.append(Vector2(0.5, 0.0)); - points.append(Vector2(0.0, overlap)); - points.append(Vector2(0.0, 1.0 - overlap)); - points.append(Vector2(0.5, 1.0)); - points.append(Vector2(1.0, 1.0 - overlap)); - points.append(Vector2(1.0, overlap)); - points.append(Vector2(0.5, 0.0)); + points.append(Vector2(0.0, -0.5)); + points.append(Vector2(-0.5, overlap - 0.5)); + points.append(Vector2(-0.5, 0.5 - overlap)); + points.append(Vector2(0.0, 0.5)); + points.append(Vector2(0.5, 0.5 - overlap)); + points.append(Vector2(0.5, overlap - 0.5)); if (get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_VERTICAL) { for (int i = 0; i < points.size(); i++) { points.write[i] = Vector2(points[i].y, points[i].x); } } } - for (int i = 0; i < points.size(); i++) { - points.write[i] = points[i] * tile_size - tile_size / 2; - } return points; } -void TileSet::draw_tile_shape(CanvasItem *p_canvas_item, Rect2 p_region, Color p_color, bool p_filled, Ref<Texture2D> p_texture) { +void TileSet::draw_tile_shape(CanvasItem *p_canvas_item, Transform2D p_transform, Color p_color, bool p_filled, Ref<Texture2D> p_texture) { if (tile_meshes_dirty) { - Vector<Vector2> uvs = get_tile_shape_polygon(); - for (int i = 0; i < uvs.size(); i++) { - uvs.write[i] = (uvs[i] + tile_size / 2) / tile_size; + Vector<Vector2> shape = get_tile_shape_polygon(); + Vector<Vector2> uvs; + uvs.resize(shape.size()); + for (int i = 0; i < shape.size(); i++) { + uvs.write[i] = shape[i] + Vector2(0.5, 0.5); } Vector<Color> colors; - colors.resize(uvs.size()); + colors.resize(shape.size()); colors.fill(Color(1.0, 1.0, 1.0, 1.0)); // Filled mesh. tile_filled_mesh->clear_surfaces(); Array a; a.resize(Mesh::ARRAY_MAX); - a[Mesh::ARRAY_VERTEX] = uvs; + a[Mesh::ARRAY_VERTEX] = shape; a[Mesh::ARRAY_TEX_UV] = uvs; a[Mesh::ARRAY_COLOR] = colors; - a[Mesh::ARRAY_INDEX] = Geometry2D::triangulate_polygon(uvs); + a[Mesh::ARRAY_INDEX] = Geometry2D::triangulate_polygon(shape); tile_filled_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, a, Array(), Dictionary(), Mesh::ARRAY_FLAG_USE_2D_VERTICES); // Lines mesh. @@ -910,22 +1048,19 @@ void TileSet::draw_tile_shape(CanvasItem *p_canvas_item, Rect2 p_region, Color p a.clear(); a.resize(Mesh::ARRAY_MAX); // Add the first point again when drawing lines. - uvs.push_back(uvs[0]); + shape.push_back(shape[0]); colors.push_back(colors[0]); - a[Mesh::ARRAY_VERTEX] = uvs; + a[Mesh::ARRAY_VERTEX] = shape; a[Mesh::ARRAY_COLOR] = colors; tile_lines_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_LINE_STRIP, a, Array(), Dictionary(), Mesh::ARRAY_FLAG_USE_2D_VERTICES); tile_meshes_dirty = false; } - Transform2D xform; - xform.scale(p_region.size); - xform.set_origin(p_region.get_position()); if (p_filled) { - p_canvas_item->draw_mesh(tile_filled_mesh, p_texture, xform, p_color); + p_canvas_item->draw_mesh(tile_filled_mesh, p_texture, p_transform, p_color); } else { - p_canvas_item->draw_mesh(tile_lines_mesh, Ref<Texture2D>(), xform, p_color); + p_canvas_item->draw_mesh(tile_lines_mesh, Ref<Texture2D>(), p_transform, p_color); } } @@ -1110,7 +1245,11 @@ Vector<Vector<Ref<Texture2D>>> TileSet::generate_terrains_icons(Size2i p_size) { if (is_valid_peering_bit_terrain(terrain_set, cell_neighbor)) { int terrain = tile_data->get_peering_bit_terrain(cell_neighbor); if (terrain >= 0) { - bit_counts[terrain] += 1; + if (terrain >= (int)bit_counts.size()) { + WARN_PRINT(vformat("Invalid peering bit terrain: %d", terrain)); + } else { + bit_counts[terrain] += 1; + } } } } @@ -1775,8 +1914,8 @@ const int TileSetSource::INVALID_TILE_ALTERNATIVE = -1; #ifndef DISABLE_DEPRECATED void TileSet::_compatibility_conversion() { - for (Map<int, CompatibilityTileData *>::Element *E = compatibility_data.front(); E; E = E->next()) { - CompatibilityTileData *ctd = E->value(); + for (KeyValue<int, CompatibilityTileData *> &E : compatibility_data) { + CompatibilityTileData *ctd = E.value; // Add the texture TileSetAtlasSource *atlas_source = memnew(TileSetAtlasSource); @@ -1814,30 +1953,30 @@ void TileSet::_compatibility_conversion() { value_array.push_back(coords); value_array.push_back(alternative_tile); - if (!compatibility_tilemap_mapping.has(E->key())) { - compatibility_tilemap_mapping[E->key()] = Map<Array, Array>(); + if (!compatibility_tilemap_mapping.has(E.key)) { + compatibility_tilemap_mapping[E.key] = Map<Array, Array>(); } - compatibility_tilemap_mapping[E->key()][key_array] = value_array; - compatibility_tilemap_mapping_tile_modes[E->key()] = COMPATIBILITY_TILE_MODE_SINGLE_TILE; + compatibility_tilemap_mapping[E.key][key_array] = value_array; + compatibility_tilemap_mapping_tile_modes[E.key] = COMPATIBILITY_TILE_MODE_SINGLE_TILE; TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(coords, alternative_tile)); tile_data->set_flip_h(flip_h); tile_data->set_flip_v(flip_v); tile_data->set_transpose(transpose); - tile_data->tile_set_material(ctd->material); + tile_data->set_material(ctd->material); tile_data->set_modulate(ctd->modulate); tile_data->set_z_index(ctd->z_index); if (ctd->occluder.is_valid()) { if (get_occlusion_layers_count() < 1) { - set_occlusion_layers_count(1); + add_occlusion_layer(); } tile_data->set_occluder(0, ctd->occluder); } if (ctd->navigation.is_valid()) { if (get_navigation_layers_count() < 1) { - set_navigation_layers_count(1); + add_navigation_layer(); } tile_data->set_navigation_polygon(0, ctd->autotile_navpoly_map[coords]); } @@ -1847,7 +1986,7 @@ void TileSet::_compatibility_conversion() { // Add the shapes. if (ctd->shapes.size() > 0) { if (get_physics_layers_count() < 1) { - set_physics_layers_count(1); + add_physics_layer(); } } for (int k = 0; k < ctd->shapes.size(); k++) { @@ -1906,29 +2045,29 @@ void TileSet::_compatibility_conversion() { value_array.push_back(coords); value_array.push_back(alternative_tile); - if (!compatibility_tilemap_mapping.has(E->key())) { - compatibility_tilemap_mapping[E->key()] = Map<Array, Array>(); + if (!compatibility_tilemap_mapping.has(E.key)) { + compatibility_tilemap_mapping[E.key] = Map<Array, Array>(); } - compatibility_tilemap_mapping[E->key()][key_array] = value_array; - compatibility_tilemap_mapping_tile_modes[E->key()] = COMPATIBILITY_TILE_MODE_ATLAS_TILE; + compatibility_tilemap_mapping[E.key][key_array] = value_array; + compatibility_tilemap_mapping_tile_modes[E.key] = COMPATIBILITY_TILE_MODE_ATLAS_TILE; TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(coords, alternative_tile)); tile_data->set_flip_h(flip_h); tile_data->set_flip_v(flip_v); tile_data->set_transpose(transpose); - tile_data->tile_set_material(ctd->material); + tile_data->set_material(ctd->material); tile_data->set_modulate(ctd->modulate); tile_data->set_z_index(ctd->z_index); if (ctd->autotile_occluder_map.has(coords)) { if (get_occlusion_layers_count() < 1) { - set_occlusion_layers_count(1); + add_occlusion_layer(); } tile_data->set_occluder(0, ctd->autotile_occluder_map[coords]); } if (ctd->autotile_navpoly_map.has(coords)) { if (get_navigation_layers_count() < 1) { - set_navigation_layers_count(1); + add_navigation_layer(); } tile_data->set_navigation_polygon(0, ctd->autotile_navpoly_map[coords]); } @@ -1942,7 +2081,7 @@ void TileSet::_compatibility_conversion() { // Add the shapes. if (ctd->shapes.size() > 0) { if (get_physics_layers_count() < 1) { - set_physics_layers_count(1); + add_physics_layer(); } } for (int k = 0; k < ctd->shapes.size(); k++) { @@ -1992,8 +2131,8 @@ void TileSet::_compatibility_conversion() { } // Reset compatibility data - for (Map<int, CompatibilityTileData *>::Element *E = compatibility_data.front(); E; E = E->next()) { - memdelete(E->get()); + for (const KeyValue<int, CompatibilityTileData *> &E : compatibility_data) { + memdelete(E.value); } compatibility_data = Map<int, CompatibilityTileData *>(); } @@ -2206,15 +2345,15 @@ bool TileSet::_set(const StringName &p_name, const Variant &p_value) { ERR_FAIL_COND_V(index < 0, false); if (components[1] == "light_mask") { ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false); - if (index >= occlusion_layers.size()) { - set_occlusion_layers_count(index + 1); + while (index >= occlusion_layers.size()) { + add_occlusion_layer(); } set_occlusion_layer_light_mask(index, p_value); return true; } else if (components[1] == "sdf_collision") { ERR_FAIL_COND_V(p_value.get_type() != Variant::BOOL, false); - if (index >= occlusion_layers.size()) { - set_occlusion_layers_count(index + 1); + while (index >= occlusion_layers.size()) { + add_occlusion_layer(); } set_occlusion_layer_sdf_collision(index, p_value); return true; @@ -2225,23 +2364,22 @@ bool TileSet::_set(const StringName &p_name, const Variant &p_value) { ERR_FAIL_COND_V(index < 0, false); if (components[1] == "collision_layer") { ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false); - if (index >= physics_layers.size()) { - set_physics_layers_count(index + 1); + while (index >= physics_layers.size()) { + add_physics_layer(); } set_physics_layer_collision_layer(index, p_value); return true; } else if (components[1] == "collision_mask") { ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false); - if (index >= physics_layers.size()) { - set_physics_layers_count(index + 1); + while (index >= physics_layers.size()) { + add_physics_layer(); } set_physics_layer_collision_mask(index, p_value); return true; } else if (components[1] == "physics_material") { Ref<PhysicsMaterial> physics_material = p_value; - ERR_FAIL_COND_V(!physics_material.is_valid(), false); - if (index >= physics_layers.size()) { - set_physics_layers_count(index + 1); + while (index >= physics_layers.size()) { + add_physics_layer(); } set_physics_layer_physics_material(index, physics_material); return true; @@ -2252,37 +2390,30 @@ bool TileSet::_set(const StringName &p_name, const Variant &p_value) { ERR_FAIL_COND_V(terrain_set_index < 0, false); if (components[1] == "mode") { ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false); - if (terrain_set_index >= terrain_sets.size()) { - set_terrain_sets_count(terrain_set_index + 1); + while (terrain_set_index >= terrain_sets.size()) { + add_terrain_set(); } set_terrain_set_mode(terrain_set_index, TerrainMode(int(p_value))); - } else if (components[1] == "terrains_count") { - ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false); - if (terrain_set_index >= terrain_sets.size()) { - set_terrain_sets_count(terrain_set_index + 1); - } - set_terrains_count(terrain_set_index, p_value); - return true; } else if (components.size() >= 3 && components[1].begins_with("terrain_") && components[1].trim_prefix("terrain_").is_valid_int()) { int terrain_index = components[1].trim_prefix("terrain_").to_int(); ERR_FAIL_COND_V(terrain_index < 0, false); if (components[2] == "name") { ERR_FAIL_COND_V(p_value.get_type() != Variant::STRING, false); - if (terrain_set_index >= terrain_sets.size()) { - set_terrain_sets_count(terrain_set_index + 1); + while (terrain_set_index >= terrain_sets.size()) { + add_terrain_set(); } - if (terrain_index >= terrain_sets[terrain_set_index].terrains.size()) { - set_terrains_count(terrain_set_index, terrain_index + 1); + while (terrain_index >= terrain_sets[terrain_set_index].terrains.size()) { + add_terrain(terrain_set_index); } set_terrain_name(terrain_set_index, terrain_index, p_value); return true; } else if (components[2] == "color") { ERR_FAIL_COND_V(p_value.get_type() != Variant::COLOR, false); - if (terrain_set_index >= terrain_sets.size()) { - set_terrain_sets_count(terrain_set_index + 1); + while (terrain_set_index >= terrain_sets.size()) { + add_terrain_set(); } - if (terrain_index >= terrain_sets[terrain_set_index].terrains.size()) { - set_terrains_count(terrain_set_index, terrain_index + 1); + while (terrain_index >= terrain_sets[terrain_set_index].terrains.size()) { + add_terrain(terrain_set_index); } set_terrain_color(terrain_set_index, terrain_index, p_value); return true; @@ -2294,8 +2425,8 @@ bool TileSet::_set(const StringName &p_name, const Variant &p_value) { ERR_FAIL_COND_V(index < 0, false); if (components[1] == "layers") { ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false); - if (index >= navigation_layers.size()) { - set_navigation_layers_count(index + 1); + while (index >= navigation_layers.size()) { + add_navigation_layer(); } set_navigation_layer_layers(index, p_value); return true; @@ -2306,15 +2437,15 @@ bool TileSet::_set(const StringName &p_name, const Variant &p_value) { ERR_FAIL_COND_V(index < 0, false); if (components[1] == "name") { ERR_FAIL_COND_V(p_value.get_type() != Variant::STRING, false); - if (index >= custom_data_layers.size()) { - set_custom_data_layers_count(index + 1); + while (index >= custom_data_layers.size()) { + add_custom_data_layer(); } set_custom_data_name(index, p_value); return true; } else if (components[1] == "type") { ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false); - if (index >= custom_data_layers.size()) { - set_custom_data_layers_count(index + 1); + while (index >= custom_data_layers.size()) { + add_custom_data_layer(); } set_custom_data_type(index, Variant::Type(int(p_value))); return true; @@ -2402,9 +2533,6 @@ bool TileSet::_get(const StringName &p_name, Variant &r_ret) const { if (components[1] == "mode") { r_ret = get_terrain_set_mode(terrain_set_index); return true; - } else if (components[1] == "terrains_count") { - r_ret = get_terrains_count(terrain_set_index); - return true; } else if (components.size() >= 3 && components[1].begins_with("terrain_") && components[1].trim_prefix("terrain_").is_valid_int()) { int terrain_index = components[1].trim_prefix("terrain_").to_int(); if (terrain_index < 0 || terrain_index >= terrain_sets[terrain_set_index].terrains.size()) { @@ -2454,25 +2582,25 @@ bool TileSet::_get(const StringName &p_name, Variant &r_ret) const { } else if (components.size() == 2 && components[0] == "tile_proxies") { if (components[1] == "source_level") { Array a; - for (Map<int, int>::Element *E = source_level_proxies.front(); E; E = E->next()) { - a.push_back(E->key()); - a.push_back(E->get()); + for (const KeyValue<int, int> &E : source_level_proxies) { + a.push_back(E.key); + a.push_back(E.value); } r_ret = a; return true; } else if (components[1] == "coords_level") { Array a; - for (Map<Array, Array>::Element *E = coords_level_proxies.front(); E; E = E->next()) { - a.push_back(E->key()); - a.push_back(E->get()); + for (const KeyValue<Array, Array> &E : coords_level_proxies) { + a.push_back(E.key); + a.push_back(E.value); } r_ret = a; return true; } else if (components[1] == "alternative_level") { Array a; - for (Map<Array, Array>::Element *E = alternative_level_proxies.front(); E; E = E->next()) { - a.push_back(E->key()); - a.push_back(E->get()); + for (const KeyValue<Array, Array> &E : alternative_level_proxies) { + a.push_back(E.key); + a.push_back(E.value); } r_ret = a; return true; @@ -2522,7 +2650,7 @@ void TileSet::_get_property_list(List<PropertyInfo> *p_list) const { p_list->push_back(PropertyInfo(Variant::NIL, "Terrains", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP)); for (int terrain_set_index = 0; terrain_set_index < terrain_sets.size(); terrain_set_index++) { p_list->push_back(PropertyInfo(Variant::INT, vformat("terrain_set_%d/mode", terrain_set_index), PROPERTY_HINT_ENUM, "Match corners and sides,Match corners,Match sides")); - p_list->push_back(PropertyInfo(Variant::INT, vformat("terrain_set_%d/terrains_count", terrain_set_index), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR)); + p_list->push_back(PropertyInfo(Variant::NIL, vformat("terrain_set_%d/terrains", terrain_set_index), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_ARRAY, vformat("terrain_set_%d/terrain_", terrain_set_index))); for (int terrain_index = 0; terrain_index < terrain_sets[terrain_set_index].terrains.size(); terrain_index++) { p_list->push_back(PropertyInfo(Variant::STRING, vformat("terrain_set_%d/terrain_%d/name", terrain_set_index, terrain_index))); p_list->push_back(PropertyInfo(Variant::COLOR, vformat("terrain_set_%d/terrain_%d/color", terrain_set_index, terrain_index))); @@ -2548,8 +2676,8 @@ void TileSet::_get_property_list(List<PropertyInfo> *p_list) const { // Sources. // Note: sources have to be listed in at the end as some TileData rely on the TileSet properties being initialized first. - for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) { - p_list->push_back(PropertyInfo(Variant::INT, vformat("sources/%d", E_source->key()), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); + for (const KeyValue<int, Ref<TileSetSource>> &E_source : sources) { + p_list->push_back(PropertyInfo(Variant::INT, vformat("sources/%d", E_source.key), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); } // Tile Proxies. @@ -2560,16 +2688,24 @@ void TileSet::_get_property_list(List<PropertyInfo> *p_list) const { p_list->push_back(PropertyInfo(Variant::ARRAY, "tile_proxies/alternative_level", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); } +void TileSet::_validate_property(PropertyInfo &property) const { + if (property.name == "tile_layout" && tile_shape == TILE_SHAPE_SQUARE) { + property.usage ^= PROPERTY_USAGE_READ_ONLY; + } else if (property.name == "tile_offset_axis" && tile_shape == TILE_SHAPE_SQUARE) { + property.usage ^= PROPERTY_USAGE_READ_ONLY; + } +} + void TileSet::_bind_methods() { // Sources management. ClassDB::bind_method(D_METHOD("get_next_source_id"), &TileSet::get_next_source_id); - ClassDB::bind_method(D_METHOD("add_source", "atlas_source_id_override"), &TileSet::add_source, DEFVAL(TileSet::INVALID_SOURCE)); + ClassDB::bind_method(D_METHOD("add_source", "source", "atlas_source_id_override"), &TileSet::add_source, DEFVAL(TileSet::INVALID_SOURCE)); ClassDB::bind_method(D_METHOD("remove_source", "source_id"), &TileSet::remove_source); - ClassDB::bind_method(D_METHOD("set_source_id", "source_id"), &TileSet::set_source_id); + ClassDB::bind_method(D_METHOD("set_source_id", "source_id", "new_source_id"), &TileSet::set_source_id); ClassDB::bind_method(D_METHOD("get_source_count"), &TileSet::get_source_count); ClassDB::bind_method(D_METHOD("get_source_id", "index"), &TileSet::get_source_id); - ClassDB::bind_method(D_METHOD("has_source", "index"), &TileSet::has_source); - ClassDB::bind_method(D_METHOD("get_source", "index"), &TileSet::get_source); + ClassDB::bind_method(D_METHOD("has_source", "source_id"), &TileSet::has_source); + ClassDB::bind_method(D_METHOD("get_source", "source_id"), &TileSet::get_source); // Shape and layout. ClassDB::bind_method(D_METHOD("set_tile_shape", "shape"), &TileSet::set_tile_shape); @@ -2590,16 +2726,20 @@ void TileSet::_bind_methods() { ClassDB::bind_method(D_METHOD("set_uv_clipping", "uv_clipping"), &TileSet::set_uv_clipping); ClassDB::bind_method(D_METHOD("is_uv_clipping"), &TileSet::is_uv_clipping); - ClassDB::bind_method(D_METHOD("set_occlusion_layers_count", "occlusion_layers_count"), &TileSet::set_occlusion_layers_count); ClassDB::bind_method(D_METHOD("get_occlusion_layers_count"), &TileSet::get_occlusion_layers_count); + ClassDB::bind_method(D_METHOD("add_occlusion_layer", "to_position"), &TileSet::add_occlusion_layer, DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("move_occlusion_layer", "layer_index", "to_position"), &TileSet::move_occlusion_layer); + ClassDB::bind_method(D_METHOD("remove_occlusion_layer", "layer_index"), &TileSet::remove_occlusion_layer); ClassDB::bind_method(D_METHOD("set_occlusion_layer_light_mask", "layer_index", "light_mask"), &TileSet::set_occlusion_layer_light_mask); - ClassDB::bind_method(D_METHOD("get_occlusion_layer_light_mask"), &TileSet::get_occlusion_layer_light_mask); + ClassDB::bind_method(D_METHOD("get_occlusion_layer_light_mask", "layer_index"), &TileSet::get_occlusion_layer_light_mask); ClassDB::bind_method(D_METHOD("set_occlusion_layer_sdf_collision", "layer_index", "sdf_collision"), &TileSet::set_occlusion_layer_sdf_collision); - ClassDB::bind_method(D_METHOD("get_occlusion_layer_sdf_collision"), &TileSet::get_occlusion_layer_sdf_collision); + ClassDB::bind_method(D_METHOD("get_occlusion_layer_sdf_collision", "layer_index"), &TileSet::get_occlusion_layer_sdf_collision); // Physics - ClassDB::bind_method(D_METHOD("set_physics_layers_count", "physics_layers_count"), &TileSet::set_physics_layers_count); ClassDB::bind_method(D_METHOD("get_physics_layers_count"), &TileSet::get_physics_layers_count); + ClassDB::bind_method(D_METHOD("add_physics_layer", "to_position"), &TileSet::add_physics_layer, DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("move_physics_layer", "layer_index", "to_position"), &TileSet::move_physics_layer); + ClassDB::bind_method(D_METHOD("remove_physics_layer", "layer_index"), &TileSet::remove_physics_layer); ClassDB::bind_method(D_METHOD("set_physics_layer_collision_layer", "layer_index", "layer"), &TileSet::set_physics_layer_collision_layer); ClassDB::bind_method(D_METHOD("get_physics_layer_collision_layer", "layer_index"), &TileSet::get_physics_layer_collision_layer); ClassDB::bind_method(D_METHOD("set_physics_layer_collision_mask", "layer_index", "mask"), &TileSet::set_physics_layer_collision_mask); @@ -2608,27 +2748,35 @@ void TileSet::_bind_methods() { ClassDB::bind_method(D_METHOD("get_physics_layer_physics_material", "layer_index"), &TileSet::get_physics_layer_physics_material); // Terrains - ClassDB::bind_method(D_METHOD("set_terrain_sets_count", "terrain_sets_count"), &TileSet::set_terrain_sets_count); ClassDB::bind_method(D_METHOD("get_terrain_sets_count"), &TileSet::get_terrain_sets_count); + ClassDB::bind_method(D_METHOD("add_terrain_set", "to_position"), &TileSet::add_terrain_set, DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("move_terrain_set", "terrain_set", "to_position"), &TileSet::move_terrain_set); + ClassDB::bind_method(D_METHOD("remove_terrain_set", "terrain_set"), &TileSet::remove_terrain_set); ClassDB::bind_method(D_METHOD("set_terrain_set_mode", "terrain_set", "mode"), &TileSet::set_terrain_set_mode); ClassDB::bind_method(D_METHOD("get_terrain_set_mode", "terrain_set"), &TileSet::get_terrain_set_mode); - ClassDB::bind_method(D_METHOD("set_terrains_count", "terrain_set", "terrains_count"), &TileSet::set_terrains_count); ClassDB::bind_method(D_METHOD("get_terrains_count", "terrain_set"), &TileSet::get_terrains_count); + ClassDB::bind_method(D_METHOD("add_terrain", "terrain_set", "to_position"), &TileSet::add_terrain, DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("move_terrain", "terrain_set", "terrain_index", "to_position"), &TileSet::move_terrain); + ClassDB::bind_method(D_METHOD("remove_terrain", "terrain_set", "terrain_index"), &TileSet::remove_terrain); ClassDB::bind_method(D_METHOD("set_terrain_name", "terrain_set", "terrain_index", "name"), &TileSet::set_terrain_name); ClassDB::bind_method(D_METHOD("get_terrain_name", "terrain_set", "terrain_index"), &TileSet::get_terrain_name); ClassDB::bind_method(D_METHOD("set_terrain_color", "terrain_set", "terrain_index", "color"), &TileSet::set_terrain_color); ClassDB::bind_method(D_METHOD("get_terrain_color", "terrain_set", "terrain_index"), &TileSet::get_terrain_color); // Navigation - ClassDB::bind_method(D_METHOD("set_navigation_layers_count", "navigation_layers_count"), &TileSet::set_navigation_layers_count); ClassDB::bind_method(D_METHOD("get_navigation_layers_count"), &TileSet::get_navigation_layers_count); + ClassDB::bind_method(D_METHOD("add_navigation_layer", "to_position"), &TileSet::add_navigation_layer, DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("move_navigation_layer", "layer_index", "to_position"), &TileSet::move_navigation_layer); + ClassDB::bind_method(D_METHOD("remove_navigation_layer", "layer_index"), &TileSet::remove_navigation_layer); ClassDB::bind_method(D_METHOD("set_navigation_layer_layers", "layer_index", "layers"), &TileSet::set_navigation_layer_layers); ClassDB::bind_method(D_METHOD("get_navigation_layer_layers", "layer_index"), &TileSet::get_navigation_layer_layers); // Custom data - ClassDB::bind_method(D_METHOD("set_custom_data_layers_count", "custom_data_layers_count"), &TileSet::set_custom_data_layers_count); ClassDB::bind_method(D_METHOD("get_custom_data_layers_count"), &TileSet::get_custom_data_layers_count); + ClassDB::bind_method(D_METHOD("add_custom_data_layer", "to_position"), &TileSet::add_custom_data_layer, DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("move_custom_data_layer", "layer_index", "to_position"), &TileSet::move_custom_data_layer); + ClassDB::bind_method(D_METHOD("remove_custom_data_layer", "layer_index"), &TileSet::remove_custom_data_layer); // Tile proxies ClassDB::bind_method(D_METHOD("set_source_level_tile_proxy", "source_from", "source_to"), &TileSet::set_source_level_tile_proxy); @@ -2653,19 +2801,19 @@ void TileSet::_bind_methods() { ADD_GROUP("Rendering", ""); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "uv_clipping"), "set_uv_clipping", "is_uv_clipping"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "occlusion_layers_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_occlusion_layers_count", "get_occlusion_layers_count"); + ADD_ARRAY("occlusion_layers", "occlusion_layer_"); ADD_GROUP("Physics", ""); - ADD_PROPERTY(PropertyInfo(Variant::INT, "physics_layers_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_physics_layers_count", "get_physics_layers_count"); + ADD_ARRAY("physics_layers", "physics_layer_"); ADD_GROUP("Terrains", ""); - ADD_PROPERTY(PropertyInfo(Variant::INT, "terrains_sets_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_terrain_sets_count", "get_terrain_sets_count"); + ADD_ARRAY("terrain_sets", "terrain_set_"); ADD_GROUP("Navigation", ""); - ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_layers_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_navigation_layers_count", "get_navigation_layers_count"); + ADD_ARRAY("navigation_layers", "navigation_layer_"); ADD_GROUP("Custom data", ""); - ADD_PROPERTY(PropertyInfo(Variant::INT, "custom_data_layers_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_custom_data_layers_count", "get_custom_data_layers_count"); + ADD_ARRAY("custom_data_layers", "custom_data_layer_"); // -- Enum binding -- BIND_ENUM_CONSTANT(TILE_SHAPE_SQUARE); @@ -2713,8 +2861,8 @@ TileSet::TileSet() { TileSet::~TileSet() { #ifndef DISABLE_DEPRECATED - for (Map<int, CompatibilityTileData *>::Element *E = compatibility_data.front(); E; E = E->next()) { - memdelete(E->get()); + for (const KeyValue<int, CompatibilityTileData *> &E : compatibility_data) { + memdelete(E.value); } #endif // DISABLE_DEPRECATED while (!source_ids.is_empty()) { @@ -2728,33 +2876,189 @@ void TileSetSource::set_tile_set(const TileSet *p_tile_set) { tile_set = p_tile_set; } +void TileSetSource::_bind_methods() { + // Base tiles + ClassDB::bind_method(D_METHOD("get_tiles_count"), &TileSetSource::get_tiles_count); + ClassDB::bind_method(D_METHOD("get_tile_id", "index"), &TileSetSource::get_tile_id); + ClassDB::bind_method(D_METHOD("has_tile", "atlas_coords"), &TileSetSource::has_tile); + + // Alternative tiles + ClassDB::bind_method(D_METHOD("get_alternative_tiles_count", "atlas_coords"), &TileSetSource::get_alternative_tiles_count); + ClassDB::bind_method(D_METHOD("get_alternative_tile_id", "atlas_coords", "index"), &TileSetSource::get_alternative_tile_id); + ClassDB::bind_method(D_METHOD("has_alternative_tile", "atlas_coords", "alternative_tile"), &TileSetSource::has_alternative_tile); +} + /////////////////////////////// TileSetAtlasSource ////////////////////////////////////// void TileSetAtlasSource::set_tile_set(const TileSet *p_tile_set) { tile_set = p_tile_set; // Set the TileSet on all TileData. - for (Map<Vector2i, TileAlternativesData>::Element *E_tile = tiles.front(); E_tile; E_tile = E_tile->next()) { - for (Map<int, TileData *>::Element *E_alternative = E_tile->get().alternatives.front(); E_alternative; E_alternative = E_alternative->next()) { - E_alternative->get()->set_tile_set(tile_set); + for (KeyValue<Vector2i, TileAlternativesData> &E_tile : tiles) { + for (KeyValue<int, TileData *> &E_alternative : E_tile.value.alternatives) { + E_alternative.value->set_tile_set(tile_set); } } } void TileSetAtlasSource::notify_tile_data_properties_should_change() { // Set the TileSet on all TileData. - for (Map<Vector2i, TileAlternativesData>::Element *E_tile = tiles.front(); E_tile; E_tile = E_tile->next()) { - for (Map<int, TileData *>::Element *E_alternative = E_tile->get().alternatives.front(); E_alternative; E_alternative = E_alternative->next()) { - E_alternative->get()->notify_tile_data_properties_should_change(); + for (KeyValue<Vector2i, TileAlternativesData> &E_tile : tiles) { + for (KeyValue<int, TileData *> &E_alternative : E_tile.value.alternatives) { + E_alternative.value->notify_tile_data_properties_should_change(); + } + } +} + +void TileSetAtlasSource::add_occlusion_layer(int p_to_pos) { + for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) { + for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) { + E_alternative.value->add_occlusion_layer(p_to_pos); + } + } +} + +void TileSetAtlasSource::move_occlusion_layer(int p_from_index, int p_to_pos) { + for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) { + for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) { + E_alternative.value->move_occlusion_layer(p_from_index, p_to_pos); + } + } +} + +void TileSetAtlasSource::remove_occlusion_layer(int p_index) { + for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) { + for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) { + E_alternative.value->remove_occlusion_layer(p_index); + } + } +} + +void TileSetAtlasSource::add_physics_layer(int p_to_pos) { + for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) { + for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) { + E_alternative.value->add_physics_layer(p_to_pos); + } + } +} + +void TileSetAtlasSource::move_physics_layer(int p_from_index, int p_to_pos) { + for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) { + for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) { + E_alternative.value->move_physics_layer(p_from_index, p_to_pos); + } + } +} + +void TileSetAtlasSource::remove_physics_layer(int p_index) { + for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) { + for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) { + E_alternative.value->remove_physics_layer(p_index); + } + } +} + +void TileSetAtlasSource::add_terrain_set(int p_to_pos) { + for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) { + for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) { + E_alternative.value->add_terrain_set(p_to_pos); + } + } +} + +void TileSetAtlasSource::move_terrain_set(int p_from_index, int p_to_pos) { + for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) { + for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) { + E_alternative.value->move_terrain_set(p_from_index, p_to_pos); + } + } +} + +void TileSetAtlasSource::remove_terrain_set(int p_index) { + for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) { + for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) { + E_alternative.value->remove_terrain_set(p_index); + } + } +} + +void TileSetAtlasSource::add_terrain(int p_terrain_set, int p_to_pos) { + for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) { + for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) { + E_alternative.value->add_terrain(p_terrain_set, p_to_pos); + } + } +} + +void TileSetAtlasSource::move_terrain(int p_terrain_set, int p_from_index, int p_to_pos) { + for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) { + for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) { + E_alternative.value->move_terrain(p_terrain_set, p_from_index, p_to_pos); + } + } +} + +void TileSetAtlasSource::remove_terrain(int p_terrain_set, int p_index) { + for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) { + for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) { + E_alternative.value->remove_terrain(p_terrain_set, p_index); + } + } +} + +void TileSetAtlasSource::add_navigation_layer(int p_to_pos) { + for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) { + for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) { + E_alternative.value->add_navigation_layer(p_to_pos); + } + } +} + +void TileSetAtlasSource::move_navigation_layer(int p_from_index, int p_to_pos) { + for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) { + for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) { + E_alternative.value->move_navigation_layer(p_from_index, p_to_pos); + } + } +} + +void TileSetAtlasSource::remove_navigation_layer(int p_index) { + for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) { + for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) { + E_alternative.value->remove_navigation_layer(p_index); + } + } +} + +void TileSetAtlasSource::add_custom_data_layer(int p_to_pos) { + for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) { + for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) { + E_alternative.value->add_custom_data_layer(p_to_pos); + } + } +} + +void TileSetAtlasSource::move_custom_data_layer(int p_from_index, int p_to_pos) { + for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) { + for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) { + E_alternative.value->move_custom_data_layer(p_from_index, p_to_pos); + } + } +} + +void TileSetAtlasSource::remove_custom_data_layer(int p_index) { + for (KeyValue<Vector2i, TileAlternativesData> E_tile : tiles) { + for (KeyValue<int, TileData *> E_alternative : E_tile.value.alternatives) { + E_alternative.value->remove_custom_data_layer(p_index); } } } void TileSetAtlasSource::reset_state() { // Reset all TileData. - for (Map<Vector2i, TileAlternativesData>::Element *E_tile = tiles.front(); E_tile; E_tile = E_tile->next()) { - for (Map<int, TileData *>::Element *E_alternative = E_tile->get().alternatives.front(); E_alternative; E_alternative = E_alternative->next()) { - E_alternative->get()->reset_state(); + for (KeyValue<Vector2i, TileAlternativesData> &E_tile : tiles) { + for (KeyValue<int, TileData *> &E_alternative : E_tile.value.alternatives) { + E_alternative.value->reset_state(); } } } @@ -2850,8 +3154,32 @@ bool TileSetAtlasSource::_set(const StringName &p_name, const Variant &p_value) // Properties. if (components[1] == "size_in_atlas") { move_tile_in_atlas(coords, coords, p_value); + return true; } else if (components[1] == "next_alternative_id") { tiles[coords].next_alternative_id = p_value; + return true; + } else if (components[1] == "animation_columns") { + set_tile_animation_columns(coords, p_value); + return true; + } else if (components[1] == "animation_separation") { + set_tile_animation_separation(coords, p_value); + return true; + } else if (components[1] == "animation_speed") { + set_tile_animation_speed(coords, p_value); + return true; + } else if (components[1] == "animation_frames_count") { + set_tile_animation_frames_count(coords, p_value); + return true; + } else if (components.size() >= 3 && components[1].begins_with("animation_frame_") && components[1].trim_prefix("animation_frame_").is_valid_int()) { + int frame = components[1].trim_prefix("animation_frame_").to_int(); + if (components[2] == "duration") { + if (frame >= get_tile_animation_frames_count(coords)) { + set_tile_animation_frames_count(coords, frame + 1); + } + set_tile_animation_frame_duration(coords, frame, p_value); + return true; + } + return false; } else if (components[1].is_valid_int()) { int alternative_id = components[1].to_int(); if (alternative_id != TileSetSource::INVALID_TILE_ALTERNATIVE) { @@ -2897,6 +3225,28 @@ bool TileSetAtlasSource::_get(const StringName &p_name, Variant &r_ret) const { } else if (components[1] == "next_alternative_id") { r_ret = tiles[coords].next_alternative_id; return true; + } else if (components[1] == "animation_columns") { + r_ret = get_tile_animation_columns(coords); + return true; + } else if (components[1] == "animation_separation") { + r_ret = get_tile_animation_separation(coords); + return true; + } else if (components[1] == "animation_speed") { + r_ret = get_tile_animation_speed(coords); + return true; + } else if (components[1] == "animation_frames_count") { + r_ret = get_tile_animation_frames_count(coords); + return true; + } else if (components.size() >= 3 && components[1].begins_with("animation_frame_") && components[1].trim_prefix("animation_frame_").is_valid_int()) { + int frame = components[1].trim_prefix("animation_frame_").to_int(); + if (frame < 0 || frame >= get_tile_animation_frames_count(coords)) { + return false; + } + if (components[2] == "duration") { + r_ret = get_tile_animation_frame_duration(coords, frame); + return true; + } + return false; } else if (components[1].is_valid_int()) { int alternative_id = components[1].to_int(); if (alternative_id != TileSetSource::INVALID_TILE_ALTERNATIVE && tiles[coords].alternatives.has(alternative_id)) { @@ -2921,45 +3271,78 @@ bool TileSetAtlasSource::_get(const StringName &p_name, Variant &r_ret) const { void TileSetAtlasSource::_get_property_list(List<PropertyInfo> *p_list) const { // Atlases data. PropertyInfo property_info; - for (Map<Vector2i, TileAlternativesData>::Element *E_tile = tiles.front(); E_tile; E_tile = E_tile->next()) { + for (const KeyValue<Vector2i, TileAlternativesData> &E_tile : tiles) { List<PropertyInfo> tile_property_list; // size_in_atlas property_info = PropertyInfo(Variant::VECTOR2I, "size_in_atlas", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR); - if (E_tile->get().size_in_atlas == Vector2i(1, 1)) { + if (E_tile.value.size_in_atlas == Vector2i(1, 1)) { property_info.usage ^= PROPERTY_USAGE_STORAGE; } tile_property_list.push_back(property_info); // next_alternative_id property_info = PropertyInfo(Variant::INT, "next_alternative_id", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR); - if (E_tile->get().next_alternative_id == 1) { + if (E_tile.value.next_alternative_id == 1) { + property_info.usage ^= PROPERTY_USAGE_STORAGE; + } + tile_property_list.push_back(property_info); + + // animation_columns. + property_info = PropertyInfo(Variant::INT, "animation_columns", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR); + if (E_tile.value.animation_columns == 0) { + property_info.usage ^= PROPERTY_USAGE_STORAGE; + } + tile_property_list.push_back(property_info); + + // animation_separation. + property_info = PropertyInfo(Variant::INT, "animation_separation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR); + if (E_tile.value.animation_separation == Vector2i()) { + property_info.usage ^= PROPERTY_USAGE_STORAGE; + } + tile_property_list.push_back(property_info); + + // animation_speed. + property_info = PropertyInfo(Variant::FLOAT, "animation_speed", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR); + if (E_tile.value.animation_speed == 1.0) { property_info.usage ^= PROPERTY_USAGE_STORAGE; } tile_property_list.push_back(property_info); - for (Map<int, TileData *>::Element *E_alternative = E_tile->get().alternatives.front(); E_alternative; E_alternative = E_alternative->next()) { + // animation_frames_count. + tile_property_list.push_back(PropertyInfo(Variant::INT, "animation_frames_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NETWORK)); + + // animation_frame_*. + bool store_durations = tiles[E_tile.key].animation_frames_durations.size() >= 2; + for (int i = 0; i < (int)tiles[E_tile.key].animation_frames_durations.size(); i++) { + property_info = PropertyInfo(Variant::FLOAT, vformat("animation_frame_%d/duration", i), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR); + if (!store_durations) { + property_info.usage ^= PROPERTY_USAGE_STORAGE; + } + tile_property_list.push_back(property_info); + } + + for (const KeyValue<int, TileData *> &E_alternative : E_tile.value.alternatives) { // Add a dummy property to show the alternative exists. - tile_property_list.push_back(PropertyInfo(Variant::INT, vformat("%d", E_alternative->key()), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); + tile_property_list.push_back(PropertyInfo(Variant::INT, vformat("%d", E_alternative.key), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); // Get the alternative tile's properties and append them to the list of properties. List<PropertyInfo> alternative_property_list; - E_alternative->get()->get_property_list(&alternative_property_list); + E_alternative.value->get_property_list(&alternative_property_list); for (PropertyInfo &alternative_property_info : alternative_property_list) { - bool valid; - Variant default_value = ClassDB::class_get_default_property_value("TileData", alternative_property_info.name, &valid); - Variant value = E_alternative->get()->get(alternative_property_info.name); - if (valid && value == default_value) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; + Variant default_value = ClassDB::class_get_default_property_value("TileData", alternative_property_info.name); + Variant value = E_alternative.value->get(alternative_property_info.name); + if (default_value.get_type() != Variant::NIL && bool(Variant::evaluate(Variant::OP_EQUAL, value, default_value))) { + alternative_property_info.usage ^= PROPERTY_USAGE_STORAGE; } - alternative_property_info.name = vformat("%s/%s", vformat("%d", E_alternative->key()), alternative_property_info.name); + alternative_property_info.name = vformat("%s/%s", vformat("%d", E_alternative.key), alternative_property_info.name); tile_property_list.push_back(alternative_property_info); } } // Add all alternative. for (PropertyInfo &tile_property_info : tile_property_list) { - tile_property_info.name = vformat("%s/%s", vformat("%d:%d", E_tile->key().x, E_tile->key().y), tile_property_info.name); + tile_property_info.name = vformat("%s/%s", vformat("%d:%d", E_tile.key.x, E_tile.key.y), tile_property_info.name); p_list->push_back(tile_property_info); } } @@ -2969,33 +3352,27 @@ void TileSetAtlasSource::create_tile(const Vector2i p_atlas_coords, const Vector // Create a tile if it does not exists. ERR_FAIL_COND(p_atlas_coords.x < 0 || p_atlas_coords.y < 0); ERR_FAIL_COND(p_size.x <= 0 || p_size.y <= 0); - for (int x = 0; x < p_size.x; x++) { - for (int y = 0; y < p_size.y; y++) { - Vector2i coords = p_atlas_coords + Vector2i(x, y); - ERR_FAIL_COND_MSG(tiles.has(coords), vformat("Cannot create tile at position %s with size %s. Already a tile present at %s.", p_atlas_coords, p_size, coords)); - } - } + + bool room_for_tile = has_room_for_tile(p_atlas_coords, p_size, 1, Vector2i(), 1); + ERR_FAIL_COND_MSG(!room_for_tile, "Cannot create tile, tiles are already present in the space the tile would cover."); + + // Initialize the tile data. + TileAlternativesData tad; + tad.size_in_atlas = p_size; + tad.animation_frames_durations.push_back(1.0); + tad.alternatives[0] = memnew(TileData); + tad.alternatives[0]->set_tile_set(tile_set); + tad.alternatives[0]->set_allow_transform(false); + tad.alternatives[0]->connect("changed", callable_mp((Resource *)this, &TileSetAtlasSource::emit_changed)); + tad.alternatives[0]->notify_property_list_changed(); + tad.alternatives_ids.append(0); // Create and resize the tile. - tiles.insert(p_atlas_coords, TileSetAtlasSource::TileAlternativesData()); + tiles.insert(p_atlas_coords, tad); tiles_ids.append(p_atlas_coords); tiles_ids.sort(); - tiles[p_atlas_coords].size_in_atlas = p_size; - tiles[p_atlas_coords].alternatives[0] = memnew(TileData); - tiles[p_atlas_coords].alternatives[0]->set_tile_set(tile_set); - tiles[p_atlas_coords].alternatives[0]->set_allow_transform(false); - tiles[p_atlas_coords].alternatives[0]->connect("changed", callable_mp((Resource *)this, &TileSetAtlasSource::emit_changed)); - tiles[p_atlas_coords].alternatives[0]->notify_property_list_changed(); - tiles[p_atlas_coords].alternatives_ids.append(0); - - // Add all covered positions to the mapping cache - for (int x = 0; x < p_size.x; x++) { - for (int y = 0; y < p_size.y; y++) { - Vector2i coords = p_atlas_coords + Vector2i(x, y); - _coords_mapping_cache[coords] = p_atlas_coords; - } - } + _create_coords_mapping_cache(p_atlas_coords); emit_signal(SNAME("changed")); } @@ -3004,18 +3381,11 @@ void TileSetAtlasSource::remove_tile(Vector2i p_atlas_coords) { ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords))); // Remove all covered positions from the mapping cache - Size2i size = tiles[p_atlas_coords].size_in_atlas; - - for (int x = 0; x < size.x; x++) { - for (int y = 0; y < size.y; y++) { - Vector2i coords = p_atlas_coords + Vector2i(x, y); - _coords_mapping_cache.erase(coords); - } - } + _clear_coords_mapping_cache(p_atlas_coords); // Free tile data. - for (Map<int, TileData *>::Element *E_tile_data = tiles[p_atlas_coords].alternatives.front(); E_tile_data; E_tile_data = E_tile_data->next()) { - memdelete(E_tile_data->get()); + for (const KeyValue<int, TileData *> &E_tile_data : tiles[p_atlas_coords].alternatives) { + memdelete(E_tile_data.value); } // Delete the tile @@ -3038,6 +3408,118 @@ Vector2i TileSetAtlasSource::get_tile_at_coords(Vector2i p_atlas_coords) const { return _coords_mapping_cache[p_atlas_coords]; } +void TileSetAtlasSource::set_tile_animation_columns(const Vector2i p_atlas_coords, int p_frame_columns) { + ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords))); + ERR_FAIL_COND(p_frame_columns < 0); + + TileAlternativesData &tad = tiles[p_atlas_coords]; + bool room_for_tile = has_room_for_tile(p_atlas_coords, tad.size_in_atlas, p_frame_columns, tad.animation_separation, tad.animation_frames_durations.size(), p_atlas_coords); + ERR_FAIL_COND_MSG(!room_for_tile, "Cannot set animation columns count, tiles are already present in the space the tile would cover."); + + _clear_coords_mapping_cache(p_atlas_coords); + + tiles[p_atlas_coords].animation_columns = p_frame_columns; + + _create_coords_mapping_cache(p_atlas_coords); + + emit_signal(SNAME("changed")); +} + +int TileSetAtlasSource::get_tile_animation_columns(const Vector2i p_atlas_coords) const { + ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), 1, vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords))); + return tiles[p_atlas_coords].animation_columns; +} + +void TileSetAtlasSource::set_tile_animation_separation(const Vector2i p_atlas_coords, const Vector2i p_separation) { + ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords))); + ERR_FAIL_COND(p_separation.x < 0 || p_separation.y < 0); + + TileAlternativesData &tad = tiles[p_atlas_coords]; + bool room_for_tile = has_room_for_tile(p_atlas_coords, tad.size_in_atlas, tad.animation_columns, p_separation, tad.animation_frames_durations.size(), p_atlas_coords); + ERR_FAIL_COND_MSG(!room_for_tile, "Cannot set animation columns count, tiles are already present in the space the tile would cover."); + + _clear_coords_mapping_cache(p_atlas_coords); + + tiles[p_atlas_coords].animation_separation = p_separation; + + _create_coords_mapping_cache(p_atlas_coords); + + emit_signal(SNAME("changed")); +} + +Vector2i TileSetAtlasSource::get_tile_animation_separation(const Vector2i p_atlas_coords) const { + ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), Vector2i(), vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords))); + return tiles[p_atlas_coords].animation_separation; +} + +void TileSetAtlasSource::set_tile_animation_speed(const Vector2i p_atlas_coords, real_t p_speed) { + ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords))); + ERR_FAIL_COND(p_speed <= 0); + + tiles[p_atlas_coords].animation_speed = p_speed; + + emit_signal(SNAME("changed")); +} + +real_t TileSetAtlasSource::get_tile_animation_speed(const Vector2i p_atlas_coords) const { + ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), 1.0, vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords))); + return tiles[p_atlas_coords].animation_speed; +} + +void TileSetAtlasSource::set_tile_animation_frames_count(const Vector2i p_atlas_coords, int p_frames_count) { + ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords))); + ERR_FAIL_COND(p_frames_count < 1); + + TileAlternativesData &tad = tiles[p_atlas_coords]; + bool room_for_tile = has_room_for_tile(p_atlas_coords, tad.size_in_atlas, tad.animation_columns, tad.animation_separation, p_frames_count, p_atlas_coords); + ERR_FAIL_COND_MSG(!room_for_tile, "Cannot set animation columns count, tiles are already present in the space the tile would cover."); + + _clear_coords_mapping_cache(p_atlas_coords); + + int old_size = tiles[p_atlas_coords].animation_frames_durations.size(); + tiles[p_atlas_coords].animation_frames_durations.resize(p_frames_count); + for (int i = old_size; i < p_frames_count; i++) { + tiles[p_atlas_coords].animation_frames_durations[i] = 1.0; + } + + _create_coords_mapping_cache(p_atlas_coords); + + notify_property_list_changed(); + + emit_signal(SNAME("changed")); +} + +int TileSetAtlasSource::get_tile_animation_frames_count(const Vector2i p_atlas_coords) const { + ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), 1, vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords))); + return tiles[p_atlas_coords].animation_frames_durations.size(); +} + +void TileSetAtlasSource::set_tile_animation_frame_duration(const Vector2i p_atlas_coords, int p_frame_index, real_t p_duration) { + ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords))); + ERR_FAIL_INDEX(p_frame_index, (int)tiles[p_atlas_coords].animation_frames_durations.size()); + ERR_FAIL_COND(p_duration <= 0.0); + + tiles[p_atlas_coords].animation_frames_durations[p_frame_index] = p_duration; + + emit_signal(SNAME("changed")); +} + +real_t TileSetAtlasSource::get_tile_animation_frame_duration(const Vector2i p_atlas_coords, int p_frame_index) const { + ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), 1, vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords))); + ERR_FAIL_INDEX_V(p_frame_index, (int)tiles[p_atlas_coords].animation_frames_durations.size(), 0.0); + return tiles[p_atlas_coords].animation_frames_durations[p_frame_index]; +} + +real_t TileSetAtlasSource::get_tile_animation_total_duration(const Vector2i p_atlas_coords) const { + ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), 1, vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords))); + + real_t sum = 0.0; + for (int frame = 0; frame < (int)tiles[p_atlas_coords].animation_frames_durations.size(); frame++) { + sum += tiles[p_atlas_coords].animation_frames_durations[frame]; + } + return sum; +} + Vector2i TileSetAtlasSource::get_tile_size_in_atlas(Vector2i p_atlas_coords) const { ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), Vector2i(-1, -1), vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords))); @@ -3053,16 +3535,46 @@ Vector2i TileSetAtlasSource::get_tile_id(int p_index) const { return tiles_ids[p_index]; } -Rect2i TileSetAtlasSource::get_tile_texture_region(Vector2i p_atlas_coords) const { +bool TileSetAtlasSource::has_room_for_tile(Vector2i p_atlas_coords, Vector2i p_size, int p_animation_columns, Vector2i p_animation_separation, int p_frames_count, Vector2i p_ignored_tile) const { + if (p_atlas_coords.x < 0 || p_atlas_coords.y < 0) { + return false; + } + if (p_size.x <= 0 || p_size.y <= 0) { + return false; + } + Size2i atlas_grid_size = get_atlas_grid_size(); + for (int frame = 0; frame < p_frames_count; frame++) { + Vector2i frame_coords = p_atlas_coords + (p_size + p_animation_separation) * ((p_animation_columns > 0) ? Vector2i(frame % p_animation_columns, frame / p_animation_columns) : Vector2i(frame, 0)); + for (int x = 0; x < p_size.x; x++) { + for (int y = 0; y < p_size.y; y++) { + Vector2i coords = frame_coords + Vector2i(x, y); + if (_coords_mapping_cache.has(coords) && _coords_mapping_cache[coords] != p_ignored_tile) { + return false; + } + if (coords.x >= atlas_grid_size.x || coords.y >= atlas_grid_size.y) { + if (!(_coords_mapping_cache.has(coords) && _coords_mapping_cache[coords] == p_ignored_tile)) { + return false; // Only accept tiles outside the atlas if they are part of the ignored tile. + } + } + } + } + } + return true; +} + +Rect2i TileSetAtlasSource::get_tile_texture_region(Vector2i p_atlas_coords, int p_frame) const { ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), Rect2i(), vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords))); + ERR_FAIL_INDEX_V(p_frame, (int)tiles[p_atlas_coords].animation_frames_durations.size(), Rect2i()); - Vector2i size_in_atlas = tiles[p_atlas_coords].size_in_atlas; + const TileAlternativesData &tad = tiles[p_atlas_coords]; + + Vector2i size_in_atlas = tad.size_in_atlas; Vector2 region_size = texture_region_size * size_in_atlas + separation * (size_in_atlas - Vector2i(1, 1)); - Vector2 origin = margins + (p_atlas_coords * (texture_region_size + separation)); + Vector2i frame_coords = p_atlas_coords + (size_in_atlas + tad.animation_separation) * ((tad.animation_columns > 0) ? Vector2i(p_frame % tad.animation_columns, p_frame / tad.animation_columns) : Vector2i(p_frame, 0)); + Vector2 origin = margins + (frame_coords * (texture_region_size + separation)); return Rect2(origin, region_size); - ; } Vector2i TileSetAtlasSource::get_tile_effective_texture_offset(Vector2i p_atlas_coords, int p_alternative_tile) const { @@ -3074,63 +3586,29 @@ Vector2i TileSetAtlasSource::get_tile_effective_texture_offset(Vector2i p_atlas_ margin = Vector2i(MAX(0, margin.x), MAX(0, margin.y)); Vector2i effective_texture_offset = Object::cast_to<TileData>(get_tile_data(p_atlas_coords, p_alternative_tile))->get_texture_offset(); if (ABS(effective_texture_offset.x) > margin.x || ABS(effective_texture_offset.y) > margin.y) { - effective_texture_offset.x = CLAMP(effective_texture_offset.x, -margin.x, margin.x); - effective_texture_offset.y = CLAMP(effective_texture_offset.y, -margin.y, margin.y); + effective_texture_offset = effective_texture_offset.clamp(-margin, margin); } return effective_texture_offset; } -bool TileSetAtlasSource::can_move_tile_in_atlas(Vector2i p_atlas_coords, Vector2i p_new_atlas_coords, Vector2i p_new_size) const { - ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), false, vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords))); - - Vector2i new_atlas_coords = (p_new_atlas_coords != INVALID_ATLAS_COORDS) ? p_new_atlas_coords : p_atlas_coords; - if (new_atlas_coords.x < 0 || new_atlas_coords.y < 0) { - return false; - } - - Vector2i size = (p_new_size != Vector2i(-1, -1)) ? p_new_size : tiles[p_atlas_coords].size_in_atlas; - ERR_FAIL_COND_V(size.x <= 0 || size.y <= 0, false); - - Size2i grid_size = get_atlas_grid_size(); - if (new_atlas_coords.x + size.x > grid_size.x || new_atlas_coords.y + size.y > grid_size.y) { - return false; - } - - Rect2i new_rect = Rect2i(new_atlas_coords, size); - // Check if the new tile can fit in the new rect. - for (int x = new_rect.position.x; x < new_rect.get_end().x; x++) { - for (int y = new_rect.position.y; y < new_rect.get_end().y; y++) { - Vector2i coords = get_tile_at_coords(Vector2i(x, y)); - if (coords != p_atlas_coords && coords != TileSetSource::INVALID_ATLAS_COORDS) { - return false; - } - } - } - - return true; -} - void TileSetAtlasSource::move_tile_in_atlas(Vector2i p_atlas_coords, Vector2i p_new_atlas_coords, Vector2i p_new_size) { - bool can_move = can_move_tile_in_atlas(p_atlas_coords, p_new_atlas_coords, p_new_size); - ERR_FAIL_COND_MSG(!can_move, vformat("Cannot move tile at position %s with size %s. Tile already present.", p_new_atlas_coords, p_new_size)); + ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords))); + + TileAlternativesData &tad = tiles[p_atlas_coords]; // Compute the actual new rect from arguments. Vector2i new_atlas_coords = (p_new_atlas_coords != INVALID_ATLAS_COORDS) ? p_new_atlas_coords : p_atlas_coords; - Vector2i size = (p_new_size != Vector2i(-1, -1)) ? p_new_size : tiles[p_atlas_coords].size_in_atlas; + Vector2i new_size = (p_new_size != Vector2i(-1, -1)) ? p_new_size : tad.size_in_atlas; - if (new_atlas_coords == p_atlas_coords && size == tiles[p_atlas_coords].size_in_atlas) { + if (new_atlas_coords == p_atlas_coords && new_size == tad.size_in_atlas) { return; } - // Remove all covered positions from the mapping cache. - Size2i old_size = tiles[p_atlas_coords].size_in_atlas; - for (int x = 0; x < old_size.x; x++) { - for (int y = 0; y < old_size.y; y++) { - Vector2i coords = p_atlas_coords + Vector2i(x, y); - _coords_mapping_cache.erase(coords); - } - } + bool room_for_tile = has_room_for_tile(new_atlas_coords, new_size, tad.animation_columns, tad.animation_separation, tad.animation_frames_durations.size(), p_atlas_coords); + ERR_FAIL_COND_MSG(!room_for_tile, vformat("Cannot move tile at position %s with size %s. Tile already present.", new_atlas_coords, new_size)); + + _clear_coords_mapping_cache(p_atlas_coords); // Move the tile and update its size. if (new_atlas_coords != p_atlas_coords) { @@ -3141,15 +3619,9 @@ void TileSetAtlasSource::move_tile_in_atlas(Vector2i p_atlas_coords, Vector2i p_ tiles_ids.append(new_atlas_coords); tiles_ids.sort(); } - tiles[new_atlas_coords].size_in_atlas = size; + tiles[new_atlas_coords].size_in_atlas = new_size; - // Add all covered positions to the mapping cache again. - for (int x = 0; x < size.x; x++) { - for (int y = 0; y < size.y; y++) { - Vector2i coords = new_atlas_coords + Vector2i(x, y); - _coords_mapping_cache[coords] = new_atlas_coords; - } - } + _create_coords_mapping_cache(new_atlas_coords); emit_signal(SNAME("changed")); } @@ -3158,8 +3630,8 @@ bool TileSetAtlasSource::has_tiles_outside_texture() { Vector2i grid_size = get_atlas_grid_size(); Vector<Vector2i> to_remove; - for (Map<Vector2i, TileSetAtlasSource::TileAlternativesData>::Element *E = tiles.front(); E; E = E->next()) { - if (E->key().x >= grid_size.x || E->key().y >= grid_size.y) { + for (const KeyValue<Vector2i, TileSetAtlasSource::TileAlternativesData> &E : tiles) { + if (E.key.x >= grid_size.x || E.key.y >= grid_size.y) { return true; } } @@ -3171,9 +3643,9 @@ void TileSetAtlasSource::clear_tiles_outside_texture() { Vector2i grid_size = get_atlas_grid_size(); Vector<Vector2i> to_remove; - for (Map<Vector2i, TileSetAtlasSource::TileAlternativesData>::Element *E = tiles.front(); E; E = E->next()) { - if (E->key().x >= grid_size.x || E->key().y >= grid_size.y) { - to_remove.append(E->key()); + for (const KeyValue<Vector2i, TileSetAtlasSource::TileAlternativesData> &E : tiles) { + if (E.key.x >= grid_size.x || E.key.y >= grid_size.y) { + to_remove.append(E.key); } } @@ -3273,45 +3745,50 @@ void TileSetAtlasSource::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D", PROPERTY_USAGE_NOEDITOR), "set_texture", "get_texture"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "margins", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_margins", "get_margins"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "separation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_separation", "get_separation"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "tile_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_texture_region_size", "get_texture_region_size"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "texture_region_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_texture_region_size", "get_texture_region_size"); // Base tiles ClassDB::bind_method(D_METHOD("create_tile", "atlas_coords", "size"), &TileSetAtlasSource::create_tile, DEFVAL(Vector2i(1, 1))); ClassDB::bind_method(D_METHOD("remove_tile", "atlas_coords"), &TileSetAtlasSource::remove_tile); // Remove a tile. If p_tile_key.alternative_tile if different from 0, remove the alternative - ClassDB::bind_method(D_METHOD("has_tile", "atlas_coords"), &TileSetAtlasSource::has_tile); - ClassDB::bind_method(D_METHOD("can_move_tile_in_atlas", "atlas_coords", "new_atlas_coords", "new_size"), &TileSetAtlasSource::can_move_tile_in_atlas, DEFVAL(INVALID_ATLAS_COORDS), DEFVAL(Vector2i(-1, -1))); ClassDB::bind_method(D_METHOD("move_tile_in_atlas", "atlas_coords", "new_atlas_coords", "new_size"), &TileSetAtlasSource::move_tile_in_atlas, DEFVAL(INVALID_ATLAS_COORDS), DEFVAL(Vector2i(-1, -1))); ClassDB::bind_method(D_METHOD("get_tile_size_in_atlas", "atlas_coords"), &TileSetAtlasSource::get_tile_size_in_atlas); - ClassDB::bind_method(D_METHOD("get_tiles_count"), &TileSetAtlasSource::get_tiles_count); - ClassDB::bind_method(D_METHOD("get_tile_id", "index"), &TileSetAtlasSource::get_tile_id); + ClassDB::bind_method(D_METHOD("has_room_for_tile", "atlas_coords", "size", "animation_columns", "animation_separation", "frames_count", "ignored_tile"), &TileSetAtlasSource::has_room_for_tile, DEFVAL(INVALID_ATLAS_COORDS)); ClassDB::bind_method(D_METHOD("get_tile_at_coords", "atlas_coords"), &TileSetAtlasSource::get_tile_at_coords); + ClassDB::bind_method(D_METHOD("set_tile_animation_columns", "atlas_coords", "frame_columns"), &TileSetAtlasSource::set_tile_animation_columns); + ClassDB::bind_method(D_METHOD("get_tile_animation_columns", "atlas_coords"), &TileSetAtlasSource::get_tile_animation_columns); + ClassDB::bind_method(D_METHOD("set_tile_animation_separation", "atlas_coords", "separation"), &TileSetAtlasSource::set_tile_animation_separation); + ClassDB::bind_method(D_METHOD("get_tile_animation_separation", "atlas_coords"), &TileSetAtlasSource::get_tile_animation_separation); + ClassDB::bind_method(D_METHOD("set_tile_animation_speed", "atlas_coords", "speed"), &TileSetAtlasSource::set_tile_animation_speed); + ClassDB::bind_method(D_METHOD("get_tile_animation_speed", "atlas_coords"), &TileSetAtlasSource::get_tile_animation_speed); + ClassDB::bind_method(D_METHOD("set_tile_animation_frames_count", "atlas_coords", "frames_count"), &TileSetAtlasSource::set_tile_animation_frames_count); + ClassDB::bind_method(D_METHOD("get_tile_animation_frames_count", "atlas_coords"), &TileSetAtlasSource::get_tile_animation_frames_count); + ClassDB::bind_method(D_METHOD("set_tile_animation_frame_duration", "atlas_coords", "frame_index", "duration"), &TileSetAtlasSource::set_tile_animation_frame_duration); + ClassDB::bind_method(D_METHOD("get_tile_animation_frame_duration", "atlas_coords", "frame_index"), &TileSetAtlasSource::get_tile_animation_frame_duration); + ClassDB::bind_method(D_METHOD("get_tile_animation_total_duration", "atlas_coords"), &TileSetAtlasSource::get_tile_animation_total_duration); + // Alternative tiles ClassDB::bind_method(D_METHOD("create_alternative_tile", "atlas_coords", "alternative_id_override"), &TileSetAtlasSource::create_alternative_tile, DEFVAL(INVALID_TILE_ALTERNATIVE)); ClassDB::bind_method(D_METHOD("remove_alternative_tile", "atlas_coords", "alternative_tile"), &TileSetAtlasSource::remove_alternative_tile); ClassDB::bind_method(D_METHOD("set_alternative_tile_id", "atlas_coords", "alternative_tile", "new_id"), &TileSetAtlasSource::set_alternative_tile_id); - ClassDB::bind_method(D_METHOD("has_alternative_tile", "atlas_coords", "alternative_tile"), &TileSetAtlasSource::has_alternative_tile); ClassDB::bind_method(D_METHOD("get_next_alternative_tile_id", "atlas_coords"), &TileSetAtlasSource::get_next_alternative_tile_id); - ClassDB::bind_method(D_METHOD("get_alternative_tiles_count", "atlas_coords"), &TileSetAtlasSource::get_alternative_tiles_count); - ClassDB::bind_method(D_METHOD("get_alternative_tile_id", "atlas_coords", "index"), &TileSetAtlasSource::get_alternative_tile_id); - - ClassDB::bind_method(D_METHOD("get_tile_data", "atlas_coords", "index"), &TileSetAtlasSource::get_tile_data); + ClassDB::bind_method(D_METHOD("get_tile_data", "atlas_coords", "alternative_tile"), &TileSetAtlasSource::get_tile_data); // Helpers. ClassDB::bind_method(D_METHOD("get_atlas_grid_size"), &TileSetAtlasSource::get_atlas_grid_size); ClassDB::bind_method(D_METHOD("has_tiles_outside_texture"), &TileSetAtlasSource::has_tiles_outside_texture); ClassDB::bind_method(D_METHOD("clear_tiles_outside_texture"), &TileSetAtlasSource::clear_tiles_outside_texture); - ClassDB::bind_method(D_METHOD("get_tile_texture_region", "atlas_coords"), &TileSetAtlasSource::get_tile_texture_region); + ClassDB::bind_method(D_METHOD("get_tile_texture_region", "atlas_coords", "frame"), &TileSetAtlasSource::get_tile_texture_region, DEFVAL(0)); } TileSetAtlasSource::~TileSetAtlasSource() { // Free everything needed. - for (Map<Vector2i, TileAlternativesData>::Element *E_alternatives = tiles.front(); E_alternatives; E_alternatives = E_alternatives->next()) { - for (Map<int, TileData *>::Element *E_tile_data = E_alternatives->get().alternatives.front(); E_tile_data; E_tile_data = E_tile_data->next()) { - memdelete(E_tile_data->get()); + for (KeyValue<Vector2i, TileAlternativesData> &E_alternatives : tiles) { + for (KeyValue<int, TileData *> &E_tile_data : E_alternatives.value.alternatives) { + memdelete(E_tile_data.value); } } } @@ -3338,6 +3815,45 @@ void TileSetAtlasSource::_compute_next_alternative_id(const Vector2i p_atlas_coo }; } +void TileSetAtlasSource::_clear_coords_mapping_cache(Vector2i p_atlas_coords) { + ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords))); + TileAlternativesData &tad = tiles[p_atlas_coords]; + for (int frame = 0; frame < (int)tad.animation_frames_durations.size(); frame++) { + Vector2i frame_coords = p_atlas_coords + (tad.size_in_atlas + tad.animation_separation) * ((tad.animation_columns > 0) ? Vector2i(frame % tad.animation_columns, frame / tad.animation_columns) : Vector2i(frame, 0)); + for (int x = 0; x < tad.size_in_atlas.x; x++) { + for (int y = 0; y < tad.size_in_atlas.y; y++) { + Vector2i coords = frame_coords + Vector2i(x, y); + if (!_coords_mapping_cache.has(coords)) { + WARN_PRINT(vformat("TileSetAtlasSource has no cached tile at position %s, the position cache might be corrupted.", coords)); + } else { + if (_coords_mapping_cache[coords] != p_atlas_coords) { + WARN_PRINT(vformat("The position cache at position %s is pointing to a wrong tile, the position cache might be corrupted.", coords)); + } + _coords_mapping_cache.erase(coords); + } + } + } + } +} + +void TileSetAtlasSource::_create_coords_mapping_cache(Vector2i p_atlas_coords) { + ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords))); + + TileAlternativesData &tad = tiles[p_atlas_coords]; + for (int frame = 0; frame < (int)tad.animation_frames_durations.size(); frame++) { + Vector2i frame_coords = p_atlas_coords + (tad.size_in_atlas + tad.animation_separation) * ((tad.animation_columns > 0) ? Vector2i(frame % tad.animation_columns, frame / tad.animation_columns) : Vector2i(frame, 0)); + for (int x = 0; x < tad.size_in_atlas.x; x++) { + for (int y = 0; y < tad.size_in_atlas.y; y++) { + Vector2i coords = frame_coords + Vector2i(x, y); + if (_coords_mapping_cache.has(coords)) { + WARN_PRINT(vformat("The cache already has a tile for position %s, the position cache might be corrupted.", coords)); + } + _coords_mapping_cache[coords] = p_atlas_coords; + } + } + } +} + /////////////////////////////// TileSetScenesCollectionSource ////////////////////////////////////// void TileSetScenesCollectionSource::_compute_next_alternative_id() { @@ -3511,16 +4027,6 @@ void TileSetScenesCollectionSource::_get_property_list(List<PropertyInfo> *p_lis } void TileSetScenesCollectionSource::_bind_methods() { - // Base tiles - ClassDB::bind_method(D_METHOD("get_tiles_count"), &TileSetScenesCollectionSource::get_tiles_count); - ClassDB::bind_method(D_METHOD("get_tile_id", "index"), &TileSetScenesCollectionSource::get_tile_id); - ClassDB::bind_method(D_METHOD("has_tile", "atlas_coords"), &TileSetScenesCollectionSource::has_tile); - - // Alternative tiles - ClassDB::bind_method(D_METHOD("get_alternative_tiles_count", "atlas_coords"), &TileSetScenesCollectionSource::get_alternative_tiles_count); - ClassDB::bind_method(D_METHOD("get_alternative_tile_id", "atlas_coords", "index"), &TileSetScenesCollectionSource::get_alternative_tile_id); - ClassDB::bind_method(D_METHOD("has_alternative_tile", "atlas_coords", "alternative_tile"), &TileSetScenesCollectionSource::has_alternative_tile); - ClassDB::bind_method(D_METHOD("get_scene_tiles_count"), &TileSetScenesCollectionSource::get_scene_tiles_count); ClassDB::bind_method(D_METHOD("get_scene_tile_id", "index"), &TileSetScenesCollectionSource::get_scene_tile_id); ClassDB::bind_method(D_METHOD("has_scene_tile_id", "id"), &TileSetScenesCollectionSource::has_scene_tile_id); @@ -3575,6 +4081,155 @@ void TileData::notify_tile_data_properties_should_change() { emit_signal(SNAME("changed")); } +void TileData::add_occlusion_layer(int p_to_pos) { + if (p_to_pos < 0) { + p_to_pos = occluders.size(); + } + ERR_FAIL_INDEX(p_to_pos, occluders.size() + 1); + occluders.insert(p_to_pos, Ref<OccluderPolygon2D>()); +} + +void TileData::move_occlusion_layer(int p_from_index, int p_to_pos) { + ERR_FAIL_INDEX(p_from_index, occluders.size()); + ERR_FAIL_INDEX(p_to_pos, occluders.size() + 1); + occluders.insert(p_to_pos, occluders[p_from_index]); + occluders.remove(p_to_pos < p_from_index ? p_from_index + 1 : p_from_index); +} + +void TileData::remove_occlusion_layer(int p_index) { + ERR_FAIL_INDEX(p_index, occluders.size()); + occluders.remove(p_index); +} + +void TileData::add_physics_layer(int p_to_pos) { + if (p_to_pos < 0) { + p_to_pos = physics.size(); + } + ERR_FAIL_INDEX(p_to_pos, physics.size() + 1); + physics.insert(p_to_pos, PhysicsLayerTileData()); +} + +void TileData::move_physics_layer(int p_from_index, int p_to_pos) { + ERR_FAIL_INDEX(p_from_index, physics.size()); + ERR_FAIL_INDEX(p_to_pos, physics.size() + 1); + physics.insert(p_to_pos, physics[p_from_index]); + physics.remove(p_to_pos < p_from_index ? p_from_index + 1 : p_from_index); +} + +void TileData::remove_physics_layer(int p_index) { + ERR_FAIL_INDEX(p_index, physics.size()); + physics.remove(p_index); +} + +void TileData::add_terrain_set(int p_to_pos) { + if (p_to_pos >= 0 && p_to_pos <= terrain_set) { + terrain_set += 1; + } +} + +void TileData::move_terrain_set(int p_from_index, int p_to_pos) { + if (p_from_index == terrain_set) { + terrain_set = (p_from_index < p_to_pos) ? p_to_pos - 1 : p_to_pos; + } else { + if (p_from_index < terrain_set) { + terrain_set -= 1; + } + if (p_to_pos <= terrain_set) { + terrain_set += 1; + } + } +} + +void TileData::remove_terrain_set(int p_index) { + if (p_index == terrain_set) { + terrain_set = -1; + for (int i = 0; i < 16; i++) { + terrain_peering_bits[i] = -1; + } + } else if (terrain_set > p_index) { + terrain_set -= 1; + } +} + +void TileData::add_terrain(int p_terrain_set, int p_to_pos) { + if (terrain_set == p_terrain_set) { + for (int i = 0; i < 16; i++) { + if (p_to_pos >= 0 && p_to_pos <= terrain_peering_bits[i]) { + terrain_peering_bits[i] += 1; + } + } + } +} + +void TileData::move_terrain(int p_terrain_set, int p_from_index, int p_to_pos) { + if (terrain_set == p_terrain_set) { + for (int i = 0; i < 16; i++) { + if (p_from_index == terrain_peering_bits[i]) { + terrain_peering_bits[i] = (p_from_index < p_to_pos) ? p_to_pos - 1 : p_to_pos; + } else { + if (p_from_index < terrain_peering_bits[i]) { + terrain_peering_bits[i] -= 1; + } + if (p_to_pos <= terrain_peering_bits[i]) { + terrain_peering_bits[i] += 1; + } + } + } + } +} + +void TileData::remove_terrain(int p_terrain_set, int p_index) { + if (terrain_set == p_terrain_set) { + for (int i = 0; i < 16; i++) { + if (terrain_peering_bits[i] == p_index) { + terrain_peering_bits[i] = -1; + } else if (terrain_peering_bits[i] > p_index) { + terrain_peering_bits[i] -= 1; + } + } + } +} + +void TileData::add_navigation_layer(int p_to_pos) { + if (p_to_pos < 0) { + p_to_pos = navigation.size(); + } + ERR_FAIL_INDEX(p_to_pos, navigation.size() + 1); + navigation.insert(p_to_pos, Ref<NavigationPolygon>()); +} + +void TileData::move_navigation_layer(int p_from_index, int p_to_pos) { + ERR_FAIL_INDEX(p_from_index, navigation.size()); + ERR_FAIL_INDEX(p_to_pos, navigation.size() + 1); + navigation.insert(p_to_pos, navigation[p_from_index]); + navigation.remove(p_to_pos < p_from_index ? p_from_index + 1 : p_from_index); +} + +void TileData::remove_navigation_layer(int p_index) { + ERR_FAIL_INDEX(p_index, navigation.size()); + navigation.remove(p_index); +} + +void TileData::add_custom_data_layer(int p_to_pos) { + if (p_to_pos < 0) { + p_to_pos = custom_data.size(); + } + ERR_FAIL_INDEX(p_to_pos, custom_data.size() + 1); + custom_data.insert(p_to_pos, Variant()); +} + +void TileData::move_custom_data_layer(int p_from_index, int p_to_pos) { + ERR_FAIL_INDEX(p_from_index, custom_data.size()); + ERR_FAIL_INDEX(p_to_pos, custom_data.size() + 1); + custom_data.insert(p_to_pos, navigation[p_from_index]); + custom_data.remove(p_to_pos < p_from_index ? p_from_index + 1 : p_from_index); +} + +void TileData::remove_custom_data_layer(int p_index) { + ERR_FAIL_INDEX(p_index, custom_data.size()); + custom_data.remove(p_index); +} + void TileData::reset_state() { occluders.clear(); physics.clear(); @@ -3628,11 +4283,11 @@ Vector2i TileData::get_texture_offset() const { return tex_offset; } -void TileData::tile_set_material(Ref<ShaderMaterial> p_material) { +void TileData::set_material(Ref<ShaderMaterial> p_material) { material = p_material; emit_signal(SNAME("changed")); } -Ref<ShaderMaterial> TileData::tile_get_material() const { +Ref<ShaderMaterial> TileData::get_material() const { return material; } @@ -3672,9 +4327,26 @@ Ref<OccluderPolygon2D> TileData::get_occluder(int p_layer_id) const { } // Physics -int TileData::get_collision_polygons_count(int p_layer_id) const { - ERR_FAIL_INDEX_V(p_layer_id, physics.size(), 0); - return physics[p_layer_id].polygons.size(); +void TileData::set_constant_linear_velocity(int p_layer_id, const Vector2 &p_velocity) { + ERR_FAIL_INDEX(p_layer_id, physics.size()); + physics.write[p_layer_id].linear_velocity = p_velocity; + emit_signal(SNAME("changed")); +} + +Vector2 TileData::get_constant_linear_velocity(int p_layer_id) const { + ERR_FAIL_INDEX_V(p_layer_id, physics.size(), Vector2()); + return physics[p_layer_id].linear_velocity; +} + +void TileData::set_constant_angular_velocity(int p_layer_id, real_t p_velocity) { + ERR_FAIL_INDEX(p_layer_id, physics.size()); + physics.write[p_layer_id].angular_velocity = p_velocity; + emit_signal(SNAME("changed")); +} + +real_t TileData::get_constant_angular_velocity(int p_layer_id) const { + ERR_FAIL_INDEX_V(p_layer_id, physics.size(), 0.0); + return physics[p_layer_id].angular_velocity; } void TileData::set_collision_polygons_count(int p_layer_id, int p_polygons_count) { @@ -3685,6 +4357,11 @@ void TileData::set_collision_polygons_count(int p_layer_id, int p_polygons_count emit_signal(SNAME("changed")); } +int TileData::get_collision_polygons_count(int p_layer_id) const { + ERR_FAIL_INDEX_V(p_layer_id, physics.size(), 0); + return physics[p_layer_id].polygons.size(); +} + void TileData::add_collision_polygon(int p_layer_id) { ERR_FAIL_INDEX(p_layer_id, physics.size()); physics.write[p_layer_id].polygons.push_back(PhysicsLayerTileData::PolygonShapeTileData()); @@ -3886,11 +4563,7 @@ bool TileData::_set(const StringName &p_name, const Variant &p_value) { // Physics layers. int layer_index = components[0].trim_prefix("physics_layer_").to_int(); ERR_FAIL_COND_V(layer_index < 0, false); - if (components.size() == 2 && components[1] == "polygons_count") { - if (p_value.get_type() != Variant::INT) { - return false; - } - + if (components.size() == 2) { if (layer_index >= physics.size()) { if (tile_set) { return false; @@ -3898,8 +4571,19 @@ bool TileData::_set(const StringName &p_name, const Variant &p_value) { physics.resize(layer_index + 1); } } - set_collision_polygons_count(layer_index, p_value); - return true; + if (components[1] == "linear_velocity") { + set_constant_linear_velocity(layer_index, p_value); + return true; + } else if (components[1] == "angular_velocity") { + set_constant_angular_velocity(layer_index, p_value); + return true; + } else if (components[1] == "polygons_count") { + if (p_value.get_type() != Variant::INT) { + return false; + } + set_collision_polygons_count(layer_index, p_value); + return true; + } } else if (components.size() == 3 && components[1].begins_with("polygon_") && components[1].trim_prefix("polygon_").is_valid_int()) { int polygon_index = components[1].trim_prefix("polygon_").to_int(); ERR_FAIL_COND_V(polygon_index < 0, false); @@ -4001,9 +4685,18 @@ bool TileData::_get(const StringName &p_name, Variant &r_ret) const { if (layer_index >= physics.size()) { return false; } - if (components.size() == 2 && components[1] == "polygons_count") { - r_ret = get_collision_polygons_count(layer_index); - return true; + + if (components.size() == 2) { + if (components[1] == "linear_velocity") { + r_ret = get_constant_linear_velocity(layer_index); + return true; + } else if (components[1] == "angular_velocity") { + r_ret = get_constant_angular_velocity(layer_index); + return true; + } else if (components[1] == "polygons_count") { + r_ret = get_collision_polygons_count(layer_index); + return true; + } } else if (components.size() == 3 && components[1].begins_with("polygon_") && components[1].trim_prefix("polygon_").is_valid_int()) { int polygon_index = components[1].trim_prefix("polygon_").to_int(); ERR_FAIL_COND_V(polygon_index < 0, false); @@ -4074,6 +4767,8 @@ void TileData::_get_property_list(List<PropertyInfo> *p_list) const { // Physics layers. p_list->push_back(PropertyInfo(Variant::NIL, "Physics", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP)); for (int i = 0; i < physics.size(); i++) { + p_list->push_back(PropertyInfo(Variant::VECTOR2, vformat("physics_layer_%d/linear_velocity", i), PROPERTY_HINT_NONE)); + p_list->push_back(PropertyInfo(Variant::FLOAT, vformat("physics_layer_%d/angular_velocity", i), PROPERTY_HINT_NONE)); p_list->push_back(PropertyInfo(Variant::INT, vformat("physics_layer_%d/polygons_count", i), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR)); for (int j = 0; j < physics[i].polygons.size(); j++) { @@ -4148,8 +4843,8 @@ void TileData::_bind_methods() { ClassDB::bind_method(D_METHOD("get_flip_v"), &TileData::get_flip_v); ClassDB::bind_method(D_METHOD("set_transpose", "transpose"), &TileData::set_transpose); ClassDB::bind_method(D_METHOD("get_transpose"), &TileData::get_transpose); - ClassDB::bind_method(D_METHOD("tile_set_material", "material"), &TileData::tile_set_material); - ClassDB::bind_method(D_METHOD("tile_get_material"), &TileData::tile_get_material); + ClassDB::bind_method(D_METHOD("set_material", "material"), &TileData::set_material); + ClassDB::bind_method(D_METHOD("get_material"), &TileData::get_material); ClassDB::bind_method(D_METHOD("set_texture_offset", "texture_offset"), &TileData::set_texture_offset); ClassDB::bind_method(D_METHOD("get_texture_offset"), &TileData::get_texture_offset); ClassDB::bind_method(D_METHOD("set_modulate", "modulate"), &TileData::set_modulate); @@ -4163,8 +4858,12 @@ void TileData::_bind_methods() { ClassDB::bind_method(D_METHOD("get_occluder", "layer_id"), &TileData::get_occluder); // Physics. - ClassDB::bind_method(D_METHOD("get_collision_polygons_count", "layer_id"), &TileData::get_collision_polygons_count); + ClassDB::bind_method(D_METHOD("set_constant_linear_velocity", "layer_id", "velocity"), &TileData::set_constant_linear_velocity); + ClassDB::bind_method(D_METHOD("get_constant_linear_velocity", "layer_id"), &TileData::get_constant_linear_velocity); + ClassDB::bind_method(D_METHOD("set_constant_angular_velocity", "layer_id", "velocity"), &TileData::set_constant_angular_velocity); + ClassDB::bind_method(D_METHOD("get_constant_angular_velocity", "layer_id"), &TileData::get_constant_angular_velocity); ClassDB::bind_method(D_METHOD("set_collision_polygons_count", "layer_id", "polygons_count"), &TileData::set_collision_polygons_count); + ClassDB::bind_method(D_METHOD("get_collision_polygons_count", "layer_id"), &TileData::get_collision_polygons_count); ClassDB::bind_method(D_METHOD("add_collision_polygon", "layer_id"), &TileData::add_collision_polygon); ClassDB::bind_method(D_METHOD("remove_collision_polygon", "layer_id", "polygon_index"), &TileData::remove_collision_polygon); ClassDB::bind_method(D_METHOD("set_collision_polygon_points", "layer_id", "polygon_index", "polygon"), &TileData::set_collision_polygon_points); @@ -4200,6 +4899,7 @@ void TileData::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "transpose"), "set_transpose", "get_transpose"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "texture_offset"), "set_texture_offset", "get_texture_offset"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "modulate"), "set_modulate", "get_modulate"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial"), "set_material", "get_material"); ADD_PROPERTY(PropertyInfo(Variant::INT, "z_index"), "set_z_index", "get_z_index"); ADD_PROPERTY(PropertyInfo(Variant::INT, "y_sort_origin"), "set_y_sort_origin", "get_y_sort_origin"); |