diff options
author | Gilles Roudière <gilles.roudiere@gmail.com> | 2021-07-28 18:10:01 +0200 |
---|---|---|
committer | Gilles Roudière <gilles.roudiere@gmail.com> | 2021-08-02 13:54:39 +0200 |
commit | ad8b5cd5a439c341b79688dd500b8dffb5762ea3 (patch) | |
tree | 91e5ba54e49758d35bbd106070089f5c252c103a /editor/plugins/tiles/tile_map_editor.cpp | |
parent | 854725f400a2a8e2f97fb9e283d34f3325a8c3c1 (diff) |
Implements TileMap layers and move TileSetPlugins's functions to the TileMap node instead
Diffstat (limited to 'editor/plugins/tiles/tile_map_editor.cpp')
-rw-r--r-- | editor/plugins/tiles/tile_map_editor.cpp | 689 |
1 files changed, 490 insertions, 199 deletions
diff --git a/editor/plugins/tiles/tile_map_editor.cpp b/editor/plugins/tiles/tile_map_editor.cpp index e70aed8ed6..77084f551a 100644 --- a/editor/plugins/tiles/tile_map_editor.cpp +++ b/editor/plugins/tiles/tile_map_editor.cpp @@ -377,6 +377,11 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p return false; } + if (tile_map_layer < 0) { + return false; + } + ERR_FAIL_INDEX_V(tile_map_layer, tile_map->get_layers_count(), false); + Ref<TileSet> tile_set = tile_map->get_tileset(); if (!tile_set.is_valid()) { return false; @@ -391,7 +396,7 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p for (Set<Vector2i>::Element *E = tile_map_selection.front(); E; E = E->next()) { coords_array.push_back(E->get()); } - tile_map_clipboard = tile_map->get_pattern(coords_array); + tile_map_clipboard = tile_map->get_pattern(tile_map_layer, coords_array); } if (ED_IS_SHORTCUT("tiles_editor/cut", p_event)) { @@ -399,8 +404,8 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p if (!tile_map_selection.is_empty()) { undo_redo->create_action(TTR("Delete tiles")); for (Set<Vector2i>::Element *E = tile_map_selection.front(); E; E = E->next()) { - undo_redo->add_do_method(tile_map, "set_cell", E->get(), TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE); - undo_redo->add_undo_method(tile_map, "set_cell", E->get(), tile_map->get_cell_source_id(E->get()), tile_map->get_cell_atlas_coords(E->get()), tile_map->get_cell_alternative_tile(E->get())); + undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, E->get(), TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE); + undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, E->get(), tile_map->get_cell_source_id(tile_map_layer, E->get()), tile_map->get_cell_atlas_coords(tile_map_layer, E->get()), tile_map->get_cell_alternative_tile(tile_map_layer, E->get())); } undo_redo->add_undo_method(this, "_set_tile_map_selection", _get_tile_map_selection()); tile_map_selection.clear(); @@ -430,8 +435,8 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p if (!tile_map_selection.is_empty()) { undo_redo->create_action(TTR("Delete tiles")); for (Set<Vector2i>::Element *E = tile_map_selection.front(); E; E = E->next()) { - undo_redo->add_do_method(tile_map, "set_cell", E->get(), TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE); - undo_redo->add_undo_method(tile_map, "set_cell", E->get(), tile_map->get_cell_source_id(E->get()), tile_map->get_cell_atlas_coords(E->get()), tile_map->get_cell_alternative_tile(E->get())); + undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, E->get(), TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE); + undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, E->get(), tile_map->get_cell_source_id(tile_map_layer, E->get()), tile_map->get_cell_atlas_coords(tile_map_layer, E->get()), tile_map->get_cell_alternative_tile(tile_map_layer, E->get())); } undo_redo->add_undo_method(this, "_set_tile_map_selection", _get_tile_map_selection()); tile_map_selection.clear(); @@ -456,9 +461,9 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p } Vector2i coords = E->key(); if (!drag_modified.has(coords)) { - drag_modified.insert(coords, tile_map->get_cell(coords)); + drag_modified.insert(coords, tile_map->get_cell(tile_map_layer, coords)); } - tile_map->set_cell(coords, E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile); + tile_map->set_cell(tile_map_layer, coords, E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile); } } break; case DRAG_TYPE_BUCKET: { @@ -472,9 +477,9 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p } Vector2i coords = E->key(); if (!drag_modified.has(coords)) { - drag_modified.insert(coords, tile_map->get_cell(coords)); + drag_modified.insert(coords, tile_map->get_cell(tile_map_layer, coords)); } - tile_map->set_cell(coords, E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile); + tile_map->set_cell(tile_map_layer, coords, E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile); } } } @@ -497,7 +502,9 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p if (mb->get_button_index() == MOUSE_BUTTON_LEFT) { if (mb->is_pressed()) { // Pressed - if (tool_buttons_group->get_pressed_button() == select_tool_button) { + if (drag_type == DRAG_TYPE_CLIPBOARD_PASTE) { + // Do nothing. + } else if (tool_buttons_group->get_pressed_button() == select_tool_button) { drag_start_mouse_pos = mpos; if (tile_map_selection.has(tile_map->world_to_map(drag_start_mouse_pos)) && !mb->is_shift_pressed()) { // Move the selection @@ -505,8 +512,8 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p drag_modified.clear(); for (Set<Vector2i>::Element *E = tile_map_selection.front(); E; E = E->next()) { Vector2i coords = E->get(); - drag_modified.insert(coords, tile_map->get_cell(coords)); - tile_map->set_cell(coords, TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE); + drag_modified.insert(coords, tile_map->get_cell(tile_map_layer, coords)); + tile_map->set_cell(tile_map_layer, coords, TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE); } } else { // Select tiles @@ -530,9 +537,9 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p } Vector2i coords = E->key(); if (!drag_modified.has(coords)) { - drag_modified.insert(coords, tile_map->get_cell(coords)); + drag_modified.insert(coords, tile_map->get_cell(tile_map_layer, coords)); } - tile_map->set_cell(coords, E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile); + tile_map->set_cell(tile_map_layer, coords, E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile); } } else if (tool_buttons_group->get_pressed_button() == line_tool_button) { drag_type = DRAG_TYPE_LINE; @@ -556,9 +563,9 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p } Vector2i coords = E->key(); if (!drag_modified.has(coords)) { - drag_modified.insert(coords, tile_map->get_cell(coords)); + drag_modified.insert(coords, tile_map->get_cell(tile_map_layer, coords)); } - tile_map->set_cell(coords, E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile); + tile_map->set_cell(tile_map_layer, coords, E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile); } } } @@ -587,6 +594,11 @@ void TileMapEditorTilesPlugin::forward_canvas_draw_over_viewport(Control *p_over return; } + if (tile_map_layer < 0) { + return; + } + ERR_FAIL_INDEX(tile_map_layer, tile_map->get_layers_count()); + Ref<TileSet> tile_set = tile_map->get_tileset(); if (!tile_set.is_valid()) { return; @@ -623,7 +635,7 @@ void TileMapEditorTilesPlugin::forward_canvas_draw_over_viewport(Control *p_over for (int x = rect.position.x; x < rect.get_end().x; x++) { for (int y = rect.position.y; y < rect.get_end().y; y++) { Vector2i coords = Vector2i(x, y); - if (tile_map->get_cell_source_id(coords) != TileSet::INVALID_SOURCE) { + if (tile_map->get_cell_source_id(tile_map_layer, coords) != TileSet::INVALID_SOURCE) { Rect2 cell_region = xform.xform(Rect2(tile_map->map_to_world(coords) - tile_shape_size / 2, tile_shape_size)); tile_set->draw_tile_shape(p_overlay, cell_region, Color(1.0, 1.0, 1.0), false); } @@ -637,7 +649,7 @@ void TileMapEditorTilesPlugin::forward_canvas_draw_over_viewport(Control *p_over for (int x = rect.position.x; x < rect.get_end().x; x++) { for (int y = rect.position.y; y < rect.get_end().y; y++) { Vector2i coords = Vector2i(x, y); - if (tile_map->get_cell_source_id(coords) != TileSet::INVALID_SOURCE) { + if (tile_map->get_cell_source_id(tile_map_layer, coords) != TileSet::INVALID_SOURCE) { to_draw.insert(coords); } } @@ -914,6 +926,8 @@ Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_rect(Vector2i p_start TileMapPattern erase_pattern; erase_pattern.set_cell(Vector2i(0, 0), TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE); TileMapPattern *pattern = erase_button->is_pressed() ? &erase_pattern : selection_pattern; + Map<Vector2i, TileMapCell> err_output; + ERR_FAIL_COND_V(pattern->is_empty(), err_output); // Compute the offset to align things to the bottom or right. bool aligned_right = p_end_cell.x < p_start_cell.x; @@ -956,6 +970,12 @@ Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_bucket_fill(Vector2i return Map<Vector2i, TileMapCell>(); } + if (tile_map_layer < 0) { + return Map<Vector2i, TileMapCell>(); + } + Map<Vector2i, TileMapCell> output; + ERR_FAIL_INDEX_V(tile_map_layer, tile_map->get_layers_count(), output); + Ref<TileSet> tile_set = tile_map->get_tileset(); if (!tile_set.is_valid()) { return Map<Vector2i, TileMapCell>(); @@ -966,9 +986,8 @@ Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_bucket_fill(Vector2i erase_pattern.set_cell(Vector2i(0, 0), TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE); TileMapPattern *pattern = erase_button->is_pressed() ? &erase_pattern : selection_pattern; - Map<Vector2i, TileMapCell> output; if (!pattern->is_empty()) { - TileMapCell source = tile_map->get_cell(p_coords); + TileMapCell source = tile_map->get_cell(tile_map_layer, p_coords); // If we are filling empty tiles, compute the tilemap boundaries. Rect2i boundaries; @@ -985,9 +1004,9 @@ Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_bucket_fill(Vector2i Vector2i coords = to_check.back()->get(); to_check.pop_back(); if (!already_checked.has(coords)) { - if (source.source_id == tile_map->get_cell_source_id(coords) && - source.get_atlas_coords() == tile_map->get_cell_atlas_coords(coords) && - source.alternative_tile == tile_map->get_cell_alternative_tile(coords) && + if (source.source_id == tile_map->get_cell_source_id(tile_map_layer, coords) && + source.get_atlas_coords() == tile_map->get_cell_atlas_coords(tile_map_layer, coords) && + source.alternative_tile == tile_map->get_cell_alternative_tile(tile_map_layer, coords) && (source.source_id != TileSet::INVALID_SOURCE || boundaries.has_point(coords))) { if (!erase_button->is_pressed() && random_tile_checkbox->is_pressed()) { // Paint a random tile. @@ -1027,13 +1046,13 @@ Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_bucket_fill(Vector2i } } } else { - to_check = tile_map->get_used_cells(); + to_check = tile_map->get_used_cells(tile_map_layer); } for (int i = 0; i < to_check.size(); i++) { Vector2i coords = to_check[i]; - if (source.source_id == tile_map->get_cell_source_id(coords) && - source.get_atlas_coords() == tile_map->get_cell_atlas_coords(coords) && - source.alternative_tile == tile_map->get_cell_alternative_tile(coords) && + if (source.source_id == tile_map->get_cell_source_id(tile_map_layer, coords) && + source.get_atlas_coords() == tile_map->get_cell_atlas_coords(tile_map_layer, coords) && + source.alternative_tile == tile_map->get_cell_alternative_tile(tile_map_layer, coords) && (source.source_id != TileSet::INVALID_SOURCE || boundaries.has_point(coords))) { if (!erase_button->is_pressed() && random_tile_checkbox->is_pressed()) { // Paint a random tile. @@ -1066,6 +1085,11 @@ void TileMapEditorTilesPlugin::_stop_dragging() { return; } + if (tile_map_layer < 0) { + return; + } + ERR_FAIL_INDEX(tile_map_layer, tile_map->get_layers_count()); + Ref<TileSet> tile_set = tile_map->get_tileset(); if (!tile_set.is_valid()) { return; @@ -1091,7 +1115,7 @@ void TileMapEditorTilesPlugin::_stop_dragging() { tile_map_selection.erase(coords); } } else { - if (tile_map->get_cell_source_id(coords) != TileSet::INVALID_SOURCE) { + if (tile_map->get_cell_source_id(tile_map_layer, coords) != TileSet::INVALID_SOURCE) { tile_map_selection.insert(coords); } } @@ -1123,7 +1147,7 @@ void TileMapEditorTilesPlugin::_stop_dragging() { coords = tile_map->map_pattern(top_left, selection_used_cells[i], selection_pattern); cells_undo[coords] = TileMapCell(drag_modified[coords].source_id, drag_modified[coords].get_atlas_coords(), drag_modified[coords].alternative_tile); coords = tile_map->map_pattern(top_left + offset, selection_used_cells[i], selection_pattern); - cells_undo[coords] = TileMapCell(tile_map->get_cell_source_id(coords), tile_map->get_cell_atlas_coords(coords), tile_map->get_cell_alternative_tile(coords)); + cells_undo[coords] = TileMapCell(tile_map->get_cell_source_id(tile_map_layer, coords), tile_map->get_cell_atlas_coords(tile_map_layer, coords), tile_map->get_cell_alternative_tile(tile_map_layer, coords)); } Map<Vector2i, TileMapCell> cells_do; @@ -1138,10 +1162,10 @@ void TileMapEditorTilesPlugin::_stop_dragging() { undo_redo->create_action(TTR("Move tiles")); // Move the tiles. for (Map<Vector2i, TileMapCell>::Element *E = cells_do.front(); E; E = E->next()) { - undo_redo->add_do_method(tile_map, "set_cell", E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile); + undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile); } for (Map<Vector2i, TileMapCell>::Element *E = cells_undo.front(); E; E = E->next()) { - undo_redo->add_undo_method(tile_map, "set_cell", E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile); + undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile); } // Update the selection. @@ -1162,12 +1186,12 @@ void TileMapEditorTilesPlugin::_stop_dragging() { for (int x = rect.position.x; x < rect.get_end().x; x++) { for (int y = rect.position.y; y < rect.get_end().y; y++) { Vector2i coords = Vector2i(x, y); - if (tile_map->get_cell_source_id(coords) != TileSet::INVALID_SOURCE) { + if (tile_map->get_cell_source_id(tile_map_layer, coords) != TileSet::INVALID_SOURCE) { coords_array.push_back(coords); } } } - selection_pattern = tile_map->get_pattern(coords_array); + selection_pattern = tile_map->get_pattern(tile_map_layer, coords_array); if (!selection_pattern->is_empty()) { _update_tileset_selection_from_selection_pattern(); } else { @@ -1178,8 +1202,8 @@ void TileMapEditorTilesPlugin::_stop_dragging() { case DRAG_TYPE_PAINT: { undo_redo->create_action(TTR("Paint tiles")); for (Map<Vector2i, TileMapCell>::Element *E = drag_modified.front(); E; E = E->next()) { - undo_redo->add_do_method(tile_map, "set_cell", E->key(), tile_map->get_cell_source_id(E->key()), tile_map->get_cell_atlas_coords(E->key()), tile_map->get_cell_alternative_tile(E->key())); - undo_redo->add_undo_method(tile_map, "set_cell", E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile); + undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, E->key(), tile_map->get_cell_source_id(tile_map_layer, E->key()), tile_map->get_cell_atlas_coords(tile_map_layer, E->key()), tile_map->get_cell_alternative_tile(tile_map_layer, E->key())); + undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile); } undo_redo->commit_action(false); } break; @@ -1190,8 +1214,8 @@ void TileMapEditorTilesPlugin::_stop_dragging() { if (!erase_button->is_pressed() && E->get().source_id == TileSet::INVALID_SOURCE) { continue; } - undo_redo->add_do_method(tile_map, "set_cell", E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile); - undo_redo->add_undo_method(tile_map, "set_cell", E->key(), tile_map->get_cell_source_id(E->key()), tile_map->get_cell_atlas_coords(E->key()), tile_map->get_cell_alternative_tile(E->key())); + undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile); + undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, E->key(), tile_map->get_cell_source_id(tile_map_layer, E->key()), tile_map->get_cell_atlas_coords(tile_map_layer, E->key()), tile_map->get_cell_alternative_tile(tile_map_layer, E->key())); } undo_redo->commit_action(); } break; @@ -1202,16 +1226,16 @@ void TileMapEditorTilesPlugin::_stop_dragging() { if (!erase_button->is_pressed() && E->get().source_id == TileSet::INVALID_SOURCE) { continue; } - undo_redo->add_do_method(tile_map, "set_cell", E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile); - undo_redo->add_undo_method(tile_map, "set_cell", E->key(), tile_map->get_cell_source_id(E->key()), tile_map->get_cell_atlas_coords(E->key()), tile_map->get_cell_alternative_tile(E->key())); + undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile); + undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, E->key(), tile_map->get_cell_source_id(tile_map_layer, E->key()), tile_map->get_cell_atlas_coords(tile_map_layer, E->key()), tile_map->get_cell_alternative_tile(tile_map_layer, E->key())); } undo_redo->commit_action(); } break; case DRAG_TYPE_BUCKET: { undo_redo->create_action(TTR("Paint tiles")); for (Map<Vector2i, TileMapCell>::Element *E = drag_modified.front(); E; E = E->next()) { - undo_redo->add_do_method(tile_map, "set_cell", E->key(), tile_map->get_cell_source_id(E->key()), tile_map->get_cell_atlas_coords(E->key()), tile_map->get_cell_alternative_tile(E->key())); - undo_redo->add_undo_method(tile_map, "set_cell", E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile); + undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, E->key(), tile_map->get_cell_source_id(tile_map_layer, E->key()), tile_map->get_cell_atlas_coords(tile_map_layer, E->key()), tile_map->get_cell_alternative_tile(tile_map_layer, E->key())); + undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile); } undo_redo->commit_action(false); } break; @@ -1221,8 +1245,8 @@ void TileMapEditorTilesPlugin::_stop_dragging() { TypedArray<Vector2i> used_cells = tile_map_clipboard->get_used_cells(); for (int i = 0; i < used_cells.size(); i++) { Vector2i coords = tile_map->map_pattern(tile_map->world_to_map(mpos - mouse_offset), used_cells[i], tile_map_clipboard); - undo_redo->add_do_method(tile_map, "set_cell", coords, tile_map_clipboard->get_cell_source_id(used_cells[i]), tile_map_clipboard->get_cell_atlas_coords(used_cells[i]), tile_map_clipboard->get_cell_alternative_tile(used_cells[i])); - undo_redo->add_undo_method(tile_map, "set_cell", coords, tile_map->get_cell_source_id(coords), tile_map->get_cell_atlas_coords(coords), tile_map->get_cell_alternative_tile(coords)); + undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, coords, tile_map_clipboard->get_cell_source_id(used_cells[i]), tile_map_clipboard->get_cell_atlas_coords(used_cells[i]), tile_map_clipboard->get_cell_alternative_tile(used_cells[i])); + undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, coords, tile_map->get_cell_source_id(tile_map_layer, coords), tile_map->get_cell_atlas_coords(tile_map_layer, coords), tile_map->get_cell_alternative_tile(tile_map_layer, coords)); } undo_redo->commit_action(); } break; @@ -1301,13 +1325,15 @@ void TileMapEditorTilesPlugin::_update_selection_pattern_from_tilemap_selection( return; } + ERR_FAIL_INDEX(tile_map_layer, tile_map->get_layers_count()); + memdelete(selection_pattern); TypedArray<Vector2i> coords_array; for (Set<Vector2i>::Element *E = tile_map_selection.front(); E; E = E->next()) { coords_array.push_back(E->get()); } - selection_pattern = tile_map->get_pattern(coords_array); + selection_pattern = tile_map->get_pattern(tile_map_layer, coords_array); } void TileMapEditorTilesPlugin::_update_selection_pattern_from_tileset_selection() { @@ -1344,8 +1370,7 @@ void TileMapEditorTilesPlugin::_update_selection_pattern_from_tileset_selection( TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); if (atlas_source) { // Organize using coordinates. - for (List<const TileMapCell *>::Element *E_cell = E_source->get().front(); E_cell; E_cell = E_cell->next()) { - const TileMapCell *current = E_cell->get(); + for (const TileMapCell *current : E_source->get()) { if (current->alternative_tile == 0) { organized_pattern[current->get_atlas_coords()] = current; } else { @@ -1364,8 +1389,8 @@ void TileMapEditorTilesPlugin::_update_selection_pattern_from_tileset_selection( } } else { // Add everything unorganized. - for (List<const TileMapCell *>::Element *E_cell = E_source->get().front(); E_cell; E_cell = E_cell->next()) { - unorganized.push_back(E_cell->get()); + for (const TileMapCell *cell : E_source->get()) { + unorganized.push_back(cell); } } @@ -1375,8 +1400,8 @@ void TileMapEditorTilesPlugin::_update_selection_pattern_from_tileset_selection( } Vector2i organized_size = selection_pattern->get_size(); int unorganized_index = 0; - for (List<const TileMapCell *>::Element *E_cell = unorganized.front(); E_cell; E_cell = E_cell->next()) { - selection_pattern->set_cell(Vector2(organized_size.x + unorganized_index, vertical_offset), E_cell->get()->source_id, E_cell->get()->get_atlas_coords(), E_cell->get()->alternative_tile); + for (const TileMapCell *cell : unorganized) { + selection_pattern->set_cell(Vector2(organized_size.x + unorganized_index, vertical_offset), cell->source_id, cell->get_atlas_coords(), cell->alternative_tile); unorganized_index++; } vertical_offset += MAX(organized_size.y, 1); @@ -1711,13 +1736,19 @@ TypedArray<Vector2i> TileMapEditorTilesPlugin::_get_tile_map_selection() const { return output; } -void TileMapEditorTilesPlugin::edit(ObjectID p_tile_map_id) { - tile_map_id = p_tile_map_id; +void TileMapEditorTilesPlugin::edit(ObjectID p_tile_map_id, int p_tile_map_layer) { + _stop_dragging(); // Avoids staying in a wrong drag state. - // Clear the selection. - tile_set_selection.clear(); - tile_map_selection.clear(); - selection_pattern->clear(); + if (tile_map_id != p_tile_map_id) { + tile_map_id = p_tile_map_id; + + // Clear the selection. + tile_set_selection.clear(); + tile_map_selection.clear(); + selection_pattern->clear(); + } + + tile_map_layer = p_tile_map_layer; } void TileMapEditorTilesPlugin::_bind_methods() { @@ -2299,6 +2330,7 @@ Set<TileMapEditorTerrainsPlugin::Constraint> TileMapEditorTerrainsPlugin::_get_c } ERR_FAIL_INDEX_V(p_terrain_set, tile_set->get_terrain_sets_count(), Set<Constraint>()); + ERR_FAIL_INDEX_V(tile_map_layer, tile_map->get_layers_count(), Set<Constraint>()); // Build a set of dummy constraints get the constrained points. Set<Constraint> dummy_constraints; @@ -2322,7 +2354,7 @@ Set<TileMapEditorTerrainsPlugin::Constraint> TileMapEditorTerrainsPlugin::_get_c Map<Vector2i, TileSet::CellNeighbor> overlapping_terrain_bits = c.get_overlapping_coords_and_peering_bits(); for (Map<Vector2i, TileSet::CellNeighbor>::Element *E_overlapping = overlapping_terrain_bits.front(); E_overlapping; E_overlapping = E_overlapping->next()) { if (!p_to_replace.has(E_overlapping->key())) { - TileMapCell neighbor_cell = tile_map->get_cell(E_overlapping->key()); + TileMapCell neighbor_cell = tile_map->get_cell(tile_map_layer, E_overlapping->key()); TileData *neighbor_tile_data = nullptr; if (terrain_tiles.has(neighbor_cell) && terrain_tiles[neighbor_cell]->get_terrain_set() == p_terrain_set) { neighbor_tile_data = terrain_tiles[neighbor_cell]; @@ -2629,6 +2661,90 @@ Map<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_terrains(const Map return output; } +void TileMapEditorTerrainsPlugin::_stop_dragging() { + TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id)); + if (!tile_map) { + return; + } + + Transform2D xform = CanvasItemEditor::get_singleton()->get_canvas_transform() * tile_map->get_global_transform(); + Vector2 mpos = xform.affine_inverse().xform(CanvasItemEditor::get_singleton()->get_viewport_control()->get_local_mouse_position()); + + switch (drag_type) { + case DRAG_TYPE_PICK: { + Vector2i coords = tile_map->world_to_map(mpos); + TileMapCell tile = tile_map->get_cell(tile_map_layer, coords); + + if (terrain_tiles.has(tile)) { + Array terrains_tile_pattern = _build_terrains_tile_pattern(terrain_tiles[tile]); + + // Find the tree item for the right terrain set. + bool need_tree_item_switch = true; + TreeItem *tree_item = terrains_tree->get_selected(); + if (tree_item) { + Dictionary metadata_dict = tree_item->get_metadata(0); + if (metadata_dict.has("terrain_set") && metadata_dict.has("terrain_id")) { + int terrain_set = metadata_dict["terrain_set"]; + int terrain_id = metadata_dict["terrain_id"]; + if (per_terrain_terrains_tile_patterns[terrain_set][terrain_id].has(terrains_tile_pattern)) { + need_tree_item_switch = false; + } + } + } + + if (need_tree_item_switch) { + for (tree_item = terrains_tree->get_root()->get_first_child(); tree_item; tree_item = tree_item->get_next_visible()) { + Dictionary metadata_dict = tree_item->get_metadata(0); + if (metadata_dict.has("terrain_set") && metadata_dict.has("terrain_id")) { + int terrain_set = metadata_dict["terrain_set"]; + int terrain_id = metadata_dict["terrain_id"]; + if (per_terrain_terrains_tile_patterns[terrain_set][terrain_id].has(terrains_tile_pattern)) { + // Found + tree_item->select(0); + _update_tiles_list(); + break; + } + } + } + } + + // Find the list item for the given tile. + if (tree_item) { + for (int i = 0; i < terrains_tile_list->get_item_count(); i++) { + Dictionary metadata_dict = terrains_tile_list->get_item_metadata(i); + TerrainsTilePattern in_meta_terrains_tile_pattern = metadata_dict["terrains_tile_pattern"]; + bool equals = true; + for (int j = 0; j < terrains_tile_pattern.size(); j++) { + if (terrains_tile_pattern[j] != in_meta_terrains_tile_pattern[j]) { + equals = false; + break; + } + } + if (equals) { + terrains_tile_list->select(i); + break; + } + } + } else { + ERR_PRINT("Terrain tile not found."); + } + } + picker_button->set_pressed(false); + } break; + case DRAG_TYPE_PAINT: { + undo_redo->create_action(TTR("Paint terrain")); + for (Map<Vector2i, TileMapCell>::Element *E = drag_modified.front(); E; E = E->next()) { + undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, E->key(), tile_map->get_cell_source_id(tile_map_layer, E->key()), tile_map->get_cell_atlas_coords(tile_map_layer, E->key()), tile_map->get_cell_alternative_tile(tile_map_layer, E->key())); + undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile); + } + undo_redo->commit_action(false); + } break; + default: + break; + } + drag_type = DRAG_TYPE_NONE; +} + bool TileMapEditorTerrainsPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p_event) { if (!is_visible_in_tree()) { // If the bottom editor is not visible, we ignore inputs. @@ -2649,6 +2765,11 @@ bool TileMapEditorTerrainsPlugin::forward_canvas_gui_input(const Ref<InputEvent> return false; } + if (tile_map_layer < 0) { + return false; + } + ERR_FAIL_COND_V(tile_map_layer >= tile_map->get_layers_count(), false); + // Get the selected terrain. TerrainsTilePattern selected_terrains_tile_pattern; int selected_terrain_set = -1; @@ -2690,9 +2811,9 @@ bool TileMapEditorTerrainsPlugin::forward_canvas_gui_input(const Ref<InputEvent> Map<Vector2i, TileMapCell> modified = _draw_terrains(to_draw, selected_terrain_set); for (Map<Vector2i, TileMapCell>::Element *E = modified.front(); E; E = E->next()) { if (!drag_modified.has(E->key())) { - drag_modified[E->key()] = tile_map->get_cell(E->key()); + drag_modified[E->key()] = tile_map->get_cell(tile_map_layer, E->key()); } - tile_map->set_cell(E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile); + tile_map->set_cell(tile_map_layer, E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile); } } } break; @@ -2728,86 +2849,14 @@ bool TileMapEditorTerrainsPlugin::forward_canvas_gui_input(const Ref<InputEvent> Map<Vector2i, TileMapCell> to_draw = _draw_terrains(terrains_to_draw, selected_terrain_set); for (Map<Vector2i, TileMapCell>::Element *E = to_draw.front(); E; E = E->next()) { - drag_modified[E->key()] = tile_map->get_cell(E->key()); - tile_map->set_cell(E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile); + drag_modified[E->key()] = tile_map->get_cell(tile_map_layer, E->key()); + tile_map->set_cell(tile_map_layer, E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile); } } } } else { // Released - switch (drag_type) { - case DRAG_TYPE_PICK: { - Vector2i coords = tile_map->world_to_map(mpos); - TileMapCell tile = tile_map->get_cell(coords); - - if (terrain_tiles.has(tile)) { - Array terrains_tile_pattern = _build_terrains_tile_pattern(terrain_tiles[tile]); - - // Find the tree item for the right terrain set. - bool need_tree_item_switch = true; - TreeItem *tree_item = terrains_tree->get_selected(); - if (tree_item) { - Dictionary metadata_dict = tree_item->get_metadata(0); - if (metadata_dict.has("terrain_set") && metadata_dict.has("terrain_id")) { - int terrain_set = metadata_dict["terrain_set"]; - int terrain_id = metadata_dict["terrain_id"]; - if (per_terrain_terrains_tile_patterns[terrain_set][terrain_id].has(terrains_tile_pattern)) { - need_tree_item_switch = false; - } - } - } - - if (need_tree_item_switch) { - for (tree_item = terrains_tree->get_root()->get_first_child(); tree_item; tree_item = tree_item->get_next_visible()) { - Dictionary metadata_dict = tree_item->get_metadata(0); - if (metadata_dict.has("terrain_set") && metadata_dict.has("terrain_id")) { - int terrain_set = metadata_dict["terrain_set"]; - int terrain_id = metadata_dict["terrain_id"]; - if (per_terrain_terrains_tile_patterns[terrain_set][terrain_id].has(terrains_tile_pattern)) { - // Found - tree_item->select(0); - _update_tiles_list(); - break; - } - } - } - } - - // Find the list item for the given tile. - if (tree_item) { - for (int i = 0; i < terrains_tile_list->get_item_count(); i++) { - Dictionary metadata_dict = terrains_tile_list->get_item_metadata(i); - TerrainsTilePattern in_meta_terrains_tile_pattern = metadata_dict["terrains_tile_pattern"]; - bool equals = true; - for (int j = 0; j < terrains_tile_pattern.size(); j++) { - if (terrains_tile_pattern[j] != in_meta_terrains_tile_pattern[j]) { - equals = false; - break; - } - } - if (equals) { - terrains_tile_list->select(i); - break; - } - } - } else { - ERR_PRINT("Terrain tile not found."); - } - } - picker_button->set_pressed(false); - } break; - case DRAG_TYPE_PAINT: { - undo_redo->create_action(TTR("Paint terrain")); - for (Map<Vector2i, TileMapCell>::Element *E = drag_modified.front(); E; E = E->next()) { - undo_redo->add_do_method(tile_map, "set_cell", E->key(), tile_map->get_cell_source_id(E->key()), tile_map->get_cell_atlas_coords(E->key()), tile_map->get_cell_alternative_tile(E->key())); - undo_redo->add_undo_method(tile_map, "set_cell", E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile); - } - undo_redo->commit_action(false); - } break; - default: - break; - } - drag_type = DRAG_TYPE_NONE; + _stop_dragging(); } CanvasItemEditor::get_singleton()->update_viewport(); @@ -3080,8 +3129,12 @@ void TileMapEditorTerrainsPlugin::_update_tiles_list() { } } -void TileMapEditorTerrainsPlugin::edit(ObjectID p_tile_map_id) { +void TileMapEditorTerrainsPlugin::edit(ObjectID p_tile_map_id, int p_tile_map_layer) { + _stop_dragging(); // Avoids staying in a wrong drag state. + tile_map_id = p_tile_map_id; + tile_map_layer = p_tile_map_layer; + _update_terrains_cache(); _update_terrains_tree(); _update_tiles_list(); @@ -3166,10 +3219,12 @@ void TileMapEditor::_notification(int p_what) { advanced_menu_button->set_icon(get_theme_icon(SNAME("Tools"), SNAME("EditorIcons"))); toggle_grid_button->set_icon(get_theme_icon(SNAME("Grid"), SNAME("EditorIcons"))); toggle_grid_button->set_pressed(EditorSettings::get_singleton()->get("editors/tiles_editor/display_grid")); + toogle_highlight_selected_layer_button->set_icon(get_theme_icon(SNAME("TileMapHighlightSelected"), SNAME("EditorIcons"))); break; case NOTIFICATION_INTERNAL_PROCESS: if (is_visible_in_tree() && tileset_changed_needs_update) { _update_bottom_panel(); + _update_layers_selection(); tile_map_editor_plugins[tabs->get_current_tab()]->tile_set_changed(); CanvasItemEditor::get_singleton()->update_viewport(); tileset_changed_needs_update = false; @@ -3178,6 +3233,16 @@ void TileMapEditor::_notification(int p_what) { case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: toggle_grid_button->set_pressed(EditorSettings::get_singleton()->get("editors/tiles_editor/display_grid")); break; + case NOTIFICATION_VISIBILITY_CHANGED: + TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id)); + if (tile_map) { + if (is_visible_in_tree()) { + tile_map->set_selected_layer(tile_map_layer); + } else { + tile_map->set_selected_layer(-1); + } + } + break; } } @@ -3185,6 +3250,63 @@ void TileMapEditor::_on_grid_toggled(bool p_pressed) { EditorSettings::get_singleton()->set("editors/tiles_editor/display_grid", p_pressed); } +void TileMapEditor::_layers_selection_button_draw() { + if (!has_theme_icon(SNAME("arrow"), SNAME("OptionButton"))) { + return; + } + + RID ci = layers_selection_button->get_canvas_item(); + Ref<Texture2D> arrow = Control::get_theme_icon(SNAME("arrow"), SNAME("OptionButton")); + + Color clr = Color(1, 1, 1); + if (get_theme_constant(SNAME("modulate_arrow"))) { + switch (layers_selection_button->get_draw_mode()) { + case BaseButton::DRAW_PRESSED: + clr = get_theme_color(SNAME("font_pressed_color")); + break; + case BaseButton::DRAW_HOVER: + clr = get_theme_color(SNAME("font_hover_color")); + break; + case BaseButton::DRAW_DISABLED: + clr = get_theme_color(SNAME("font_disabled_color")); + break; + default: + clr = get_theme_color(SNAME("font_color")); + } + } + + Size2 size = layers_selection_button->get_size(); + + Point2 ofs; + if (is_layout_rtl()) { + ofs = Point2(get_theme_constant(SNAME("arrow_margin"), SNAME("OptionButton")), int(Math::abs((size.height - arrow->get_height()) / 2))); + } else { + ofs = Point2(size.width - arrow->get_width() - get_theme_constant(SNAME("arrow_margin"), SNAME("OptionButton")), int(Math::abs((size.height - arrow->get_height()) / 2))); + } + Rect2 dst_rect = Rect2(ofs, arrow->get_size()); + if (!layers_selection_button->is_pressed()) { + dst_rect.size = -dst_rect.size; + } + arrow->draw_rect(ci, dst_rect, false, clr); +} + +void TileMapEditor::_layers_selection_button_pressed() { + if (!layers_selection_popup->is_visible()) { + Size2 size = layers_selection_popup->get_contents_minimum_size(); + size.x = MAX(size.x, layers_selection_button->get_size().x); + layers_selection_popup->set_position(layers_selection_button->get_screen_position() - Size2(0, size.y * get_global_transform().get_scale().y)); + layers_selection_popup->set_size(size); + layers_selection_popup->popup(); + } else { + layers_selection_popup->hide(); + } +} + +void TileMapEditor::_layers_selection_id_pressed(int p_id) { + tile_map_layer = p_id; + _update_layers_selection(); +} + void TileMapEditor::_advanced_menu_button_id_pressed(int p_id) { TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id)); if (!tile_map) { @@ -3198,18 +3320,20 @@ void TileMapEditor::_advanced_menu_button_id_pressed(int p_id) { if (p_id == 0) { // Replace Tile Proxies undo_redo->create_action(TTR("Replace Tiles with Proxies")); - TypedArray<Vector2i> used_cells = tile_map->get_used_cells(); - for (int i = 0; i < used_cells.size(); i++) { - Vector2i cell_coords = used_cells[i]; - TileMapCell from = tile_map->get_cell(cell_coords); - Array to_array = tile_set->map_tile_proxy(from.source_id, from.get_atlas_coords(), from.alternative_tile); - TileMapCell to; - to.source_id = to_array[0]; - to.set_atlas_coords(to_array[1]); - to.alternative_tile = to_array[2]; - if (from != to) { - undo_redo->add_do_method(tile_map, "set_cell", cell_coords, to.source_id, to.get_atlas_coords(), to.alternative_tile); - undo_redo->add_undo_method(tile_map, "set_cell", cell_coords, from.source_id, from.get_atlas_coords(), from.alternative_tile); + for (int layer_index = 0; layer_index < tile_map->get_layers_count(); layer_index++) { + TypedArray<Vector2i> used_cells = tile_map->get_used_cells(layer_index); + for (int i = 0; i < used_cells.size(); i++) { + Vector2i cell_coords = used_cells[i]; + TileMapCell from = tile_map->get_cell(layer_index, cell_coords); + Array to_array = tile_set->map_tile_proxy(from.source_id, from.get_atlas_coords(), from.alternative_tile); + TileMapCell to; + to.source_id = to_array[0]; + to.set_atlas_coords(to_array[1]); + to.alternative_tile = to_array[2]; + if (from != to) { + undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, cell_coords, to.source_id, to.get_atlas_coords(), to.alternative_tile); + undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, cell_coords, from.source_id, from.get_atlas_coords(), from.alternative_tile); + } } } undo_redo->commit_action(); @@ -3315,7 +3439,7 @@ void TileMapEditor::_tile_map_changed() { void TileMapEditor::_tab_changed(int p_tab_id) { // Make the plugin edit the correct tilemap. - tile_map_editor_plugins[tabs->get_current_tab()]->edit(tile_map_id); + tile_map_editor_plugins[tabs->get_current_tab()]->edit(tile_map_id, tile_map_layer); // Update toolbar. for (int i = 0; i < tile_map_editor_plugins.size(); i++) { @@ -3339,7 +3463,129 @@ void TileMapEditor::_tab_changed(int p_tab_id) { CanvasItemEditor::get_singleton()->update_viewport(); } +void TileMapEditor::_layers_select_next_or_previous(bool p_next) { + TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id)); + if (!tile_map) { + return; + } + + if (tile_map->get_layers_count() < 1) { + return; + } + + if (tile_map_layer < 0) { + tile_map_layer = 0; + } + + int inc = p_next ? 1 : -1; + int origin_layer = tile_map_layer; + tile_map_layer = Math::posmod((tile_map_layer + inc), tile_map->get_layers_count()); + while (tile_map_layer != origin_layer) { + if (tile_map->is_layer_enabled(tile_map_layer)) { + break; + } + tile_map_layer = Math::posmod((tile_map_layer + inc), tile_map->get_layers_count()); + } + + _update_layers_selection(); +} + +void TileMapEditor::_update_layers_selection() { + layers_selection_popup->clear(); + + TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id)); + if (!tile_map) { + return; + } + + // Update the selected layer. + if (is_visible_in_tree() && tile_map->get_layers_count() >= 1) { + tile_map_layer = CLAMP(tile_map_layer, 0, tile_map->get_layers_count() - 1); + + // Search for an enabled layer if the current one is not. + int origin_layer = tile_map_layer; + while (tile_map_layer >= 0 && !tile_map->is_layer_enabled(tile_map_layer)) { + tile_map_layer--; + } + if (tile_map_layer < 0) { + tile_map_layer = origin_layer; + while (tile_map_layer < tile_map->get_layers_count() && !tile_map->is_layer_enabled(tile_map_layer)) { + tile_map_layer++; + } + } + if (tile_map_layer >= tile_map->get_layers_count()) { + tile_map_layer = -1; + } + } else { + tile_map_layer = -1; + } + tile_map->set_selected_layer(toogle_highlight_selected_layer_button->is_pressed() ? tile_map_layer : -1); + + // Build the list of layers. + for (int i = 0; i < tile_map->get_layers_count(); i++) { + String name = tile_map->get_layer_name(i); + layers_selection_popup->add_item(name.is_empty() ? vformat(TTR("Layer #%d"), i) : name, i); + layers_selection_popup->set_item_as_radio_checkable(i, true); + layers_selection_popup->set_item_disabled(i, !tile_map->is_layer_enabled(i)); + layers_selection_popup->set_item_checked(i, i == tile_map_layer); + } + + // Update the button label. + if (tile_map_layer >= 0) { + layers_selection_button->set_text(layers_selection_popup->get_item_text(tile_map_layer)); + } else { + layers_selection_button->set_text(TTR("Select a layer")); + } + + // Set button minimum width. + Size2 min_button_size = Size2(layers_selection_popup->get_contents_minimum_size().x, 0); + if (has_theme_icon(SNAME("arrow"), SNAME("OptionButton"))) { + Ref<Texture2D> arrow = Control::get_theme_icon(SNAME("arrow"), SNAME("OptionButton")); + min_button_size.x += arrow->get_size().x; + } + layers_selection_button->set_custom_minimum_size(min_button_size); + layers_selection_button->update(); + + tile_map_editor_plugins[tabs->get_current_tab()]->edit(tile_map_id, tile_map_layer); +} + +void TileMapEditor::_undo_redo_inspector_callback(Object *p_undo_redo, Object *p_edited, String p_property, Variant p_new_value) { + UndoRedo *undo_redo = Object::cast_to<UndoRedo>(p_undo_redo); + ERR_FAIL_COND(!undo_redo); + + TileMap *tile_map = Object::cast_to<TileMap>(p_edited); + if (tile_map) { + if (p_property == "layers_count") { + int new_layers_count = (int)p_new_value; + if (new_layers_count < tile_map->get_layers_count()) { + List<PropertyInfo> property_list; + tile_map->get_property_list(&property_list); + + for (PropertyInfo property_info : property_list) { + Vector<String> components = String(property_info.name).split("/", true, 2); + if (components.size() == 2 && components[0].begins_with("layer_") && components[0].trim_prefix("layer_").is_valid_int()) { + int index = components[0].trim_prefix("layer_").to_int(); + if (index >= new_layers_count) { + undo_redo->add_undo_property(tile_map, property_info.name, tile_map->get(property_info.name)); + } + } + } + } + } + } +} + bool TileMapEditor::forward_canvas_gui_input(const Ref<InputEvent> &p_event) { + if (ED_IS_SHORTCUT("tiles_editor/select_next_layer", p_event) && p_event->is_pressed()) { + _layers_select_next_or_previous(true); + return true; + } + + if (ED_IS_SHORTCUT("tiles_editor/select_previous_layer", p_event) && p_event->is_pressed()) { + _layers_select_next_or_previous(false); + return true; + } + return tile_map_editor_plugins[tabs->get_current_tab()]->forward_canvas_gui_input(p_event); } @@ -3363,49 +3609,52 @@ void TileMapEditor::forward_canvas_draw_over_viewport(Control *p_overlay) { Vector2i tile_shape_size = tile_set->get_tile_size(); // Draw tiles with invalid IDs in the grid. - TypedArray<Vector2i> used_cells = tile_map->get_used_cells(); - for (int i = 0; i < used_cells.size(); i++) { - Vector2i coords = used_cells[i]; - int tile_source_id = tile_map->get_cell_source_id(coords); - if (tile_source_id >= 0) { - Vector2i tile_atlas_coords = tile_map->get_cell_atlas_coords(coords); - int tile_alternative_tile = tile_map->get_cell_alternative_tile(coords); - - TileSetSource *source = nullptr; - if (tile_set->has_source(tile_source_id)) { - source = *tile_set->get_source(tile_source_id); - } - - if (!source || !source->has_tile(tile_atlas_coords) || !source->has_alternative_tile(tile_atlas_coords, tile_alternative_tile)) { - // Generate a random color from the hashed values of the tiles. - Array a = tile_set->map_tile_proxy(tile_source_id, tile_atlas_coords, tile_alternative_tile); - if (int(a[0]) == tile_source_id && Vector2i(a[1]) == tile_atlas_coords && int(a[2]) == tile_alternative_tile) { - // Only display the pattern if we have no proxy tile. - Array to_hash; - to_hash.push_back(tile_source_id); - to_hash.push_back(tile_atlas_coords); - to_hash.push_back(tile_alternative_tile); - uint32_t hash = RandomPCG(to_hash.hash()).rand(); - - Color color; - color = color.from_hsv( - (float)((hash >> 24) & 0xFF) / 256.0, - Math::lerp(0.5, 1.0, (float)((hash >> 16) & 0xFF) / 256.0), - Math::lerp(0.5, 1.0, (float)((hash >> 8) & 0xFF) / 256.0), - 0.8); - - // Draw the scaled tile. - Rect2 cell_region = xform.xform(Rect2(tile_map->map_to_world(coords) - Vector2(tile_shape_size) / 2, Vector2(tile_shape_size))); - tile_set->draw_tile_shape(p_overlay, cell_region, color, true, warning_pattern_texture); + if (tile_map_layer >= 0) { + ERR_FAIL_COND(tile_map_layer >= tile_map->get_layers_count()); + TypedArray<Vector2i> used_cells = tile_map->get_used_cells(tile_map_layer); + for (int i = 0; i < used_cells.size(); i++) { + Vector2i coords = used_cells[i]; + int tile_source_id = tile_map->get_cell_source_id(tile_map_layer, coords); + if (tile_source_id >= 0) { + Vector2i tile_atlas_coords = tile_map->get_cell_atlas_coords(tile_map_layer, coords); + int tile_alternative_tile = tile_map->get_cell_alternative_tile(tile_map_layer, coords); + + TileSetSource *source = nullptr; + if (tile_set->has_source(tile_source_id)) { + source = *tile_set->get_source(tile_source_id); } - // Draw the warning icon. - int min_axis = missing_tile_texture->get_size().min_axis(); - Vector2 icon_size; - icon_size[min_axis] = tile_set->get_tile_size()[min_axis] / 3; - icon_size[(min_axis + 1) % 2] = (icon_size[min_axis] * missing_tile_texture->get_size()[(min_axis + 1) % 2] / missing_tile_texture->get_size()[min_axis]); - Rect2 rect = Rect2(xform.xform(tile_map->map_to_world(coords)) - (icon_size * xform.get_scale() / 2), icon_size * xform.get_scale()); - p_overlay->draw_texture_rect(missing_tile_texture, rect); + if (!source || !source->has_tile(tile_atlas_coords) || !source->has_alternative_tile(tile_atlas_coords, tile_alternative_tile)) { + // Generate a random color from the hashed values of the tiles. + Array a = tile_set->map_tile_proxy(tile_source_id, tile_atlas_coords, tile_alternative_tile); + if (int(a[0]) == tile_source_id && Vector2i(a[1]) == tile_atlas_coords && int(a[2]) == tile_alternative_tile) { + // Only display the pattern if we have no proxy tile. + Array to_hash; + to_hash.push_back(tile_source_id); + to_hash.push_back(tile_atlas_coords); + to_hash.push_back(tile_alternative_tile); + uint32_t hash = RandomPCG(to_hash.hash()).rand(); + + Color color; + color = color.from_hsv( + (float)((hash >> 24) & 0xFF) / 256.0, + Math::lerp(0.5, 1.0, (float)((hash >> 16) & 0xFF) / 256.0), + Math::lerp(0.5, 1.0, (float)((hash >> 8) & 0xFF) / 256.0), + 0.8); + + // Draw the scaled tile. + Rect2 cell_region = xform.xform(Rect2(tile_map->map_to_world(coords) - Vector2(tile_shape_size) / 2, Vector2(tile_shape_size))); + tile_set->draw_tile_shape(p_overlay, cell_region, color, true, warning_pattern_texture); + } + + // Draw the warning icon. + int min_axis = missing_tile_texture->get_size().min_axis(); + Vector2 icon_size; + icon_size[min_axis] = tile_set->get_tile_size()[min_axis] / 3; + icon_size[(min_axis + 1) % 2] = (icon_size[min_axis] * missing_tile_texture->get_size()[(min_axis + 1) % 2] / missing_tile_texture->get_size()[min_axis]); + Rect2 rect = Rect2(xform.xform(tile_map->map_to_world(coords)) - (icon_size * xform.get_scale() / 2), icon_size * xform.get_scale()); + p_overlay->draw_texture_rect(missing_tile_texture, rect); + } } } } @@ -3476,14 +3725,19 @@ void TileMapEditor::edit(TileMap *p_tile_map) { return; } - // Disconnect to changes. TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id)); if (tile_map) { + // Unselect layer if we are changing tile_map. + if (tile_map != p_tile_map) { + tile_map->set_selected_layer(-1); + } + + // Disconnect to changes. tile_map->disconnect("changed", callable_mp(this, &TileMapEditor::_tile_map_changed)); } - // Change the edited object. if (p_tile_map) { + // Change the edited object. tile_map_id = p_tile_map->get_instance_id(); tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id)); // Connect to changes. @@ -3494,8 +3748,10 @@ void TileMapEditor::edit(TileMap *p_tile_map) { tile_map_id = ObjectID(); } + _update_layers_selection(); + // Call the plugins. - tile_map_editor_plugins[tabs->get_current_tab()]->edit(tile_map_id); + tile_map_editor_plugins[tabs->get_current_tab()]->edit(tile_map_id, tile_map_layer); _tile_map_changed(); } @@ -3503,6 +3759,10 @@ void TileMapEditor::edit(TileMap *p_tile_map) { TileMapEditor::TileMapEditor() { set_process_internal(true); + // Shortcuts. + ED_SHORTCUT("tiles_editor/select_next_layer", TTR("Select Next Tile Map Layer"), KEY_PAGEUP); + ED_SHORTCUT("tiles_editor/select_previous_layer", TTR("Select Previous Tile Map Layer"), KEY_PAGEDOWN); + // TileMap editor plugins tile_map_editor_plugins.push_back(memnew(TileMapEditorTilesPlugin)); tile_map_editor_plugins.push_back(memnew(TileMapEditorTerrainsPlugin)); @@ -3516,18 +3776,46 @@ TileMapEditor::TileMapEditor() { tabs->connect("tab_changed", callable_mp(this, &TileMapEditor::_tab_changed)); // --- TileMap toolbar --- - tilemap_toolbar = memnew(HBoxContainer); - tilemap_toolbar->set_h_size_flags(SIZE_EXPAND_FILL); - tilemap_toolbar->add_child(tabs); + tile_map_toolbar = memnew(HBoxContainer); + tile_map_toolbar->set_h_size_flags(SIZE_EXPAND_FILL); + + // Tabs. + tile_map_toolbar->add_child(tabs); + + // Tabs toolbars. for (int i = 0; i < tile_map_editor_plugins.size(); i++) { tile_map_editor_plugins[i]->get_toolbar()->hide(); - tilemap_toolbar->add_child(tile_map_editor_plugins[i]->get_toolbar()); + tile_map_toolbar->add_child(tile_map_editor_plugins[i]->get_toolbar()); } // Wide empty separation control. Control *h_empty_space = memnew(Control); h_empty_space->set_h_size_flags(SIZE_EXPAND_FILL); - tilemap_toolbar->add_child(h_empty_space); + tile_map_toolbar->add_child(h_empty_space); + + // Layer selector. + layers_selection_popup = memnew(PopupMenu); + layers_selection_popup->connect("id_pressed", callable_mp(this, &TileMapEditor::_layers_selection_id_pressed)); + layers_selection_popup->set_close_on_parent_focus(false); + + layers_selection_button = memnew(Button); + layers_selection_button->set_toggle_mode(true); + layers_selection_button->connect("draw", callable_mp(this, &TileMapEditor::_layers_selection_button_draw)); + layers_selection_button->connect("pressed", callable_mp(this, &TileMapEditor::_layers_selection_button_pressed)); + layers_selection_button->connect("hidden", callable_mp((Window *)layers_selection_popup, &Popup::hide)); + layers_selection_button->set_tooltip(TTR("Tile Map Layer")); + layers_selection_button->add_child(layers_selection_popup); + tile_map_toolbar->add_child(layers_selection_button); + + toogle_highlight_selected_layer_button = memnew(Button); + toogle_highlight_selected_layer_button->set_flat(true); + toogle_highlight_selected_layer_button->set_toggle_mode(true); + toogle_highlight_selected_layer_button->set_pressed(true); + toogle_highlight_selected_layer_button->connect("pressed", callable_mp(this, &TileMapEditor::_update_layers_selection)); + toogle_highlight_selected_layer_button->set_tooltip(TTR("Highlight Selected TileMap Layer")); + tile_map_toolbar->add_child(toogle_highlight_selected_layer_button); + + tile_map_toolbar->add_child(memnew(VSeparator)); // Grid toggle. toggle_grid_button = memnew(Button); @@ -3535,14 +3823,14 @@ TileMapEditor::TileMapEditor() { toggle_grid_button->set_toggle_mode(true); toggle_grid_button->set_tooltip(TTR("Toggle grid visibility.")); toggle_grid_button->connect("toggled", callable_mp(this, &TileMapEditor::_on_grid_toggled)); - tilemap_toolbar->add_child(toggle_grid_button); + tile_map_toolbar->add_child(toggle_grid_button); // Advanced settings menu button. advanced_menu_button = memnew(MenuButton); advanced_menu_button->set_flat(true); advanced_menu_button->get_popup()->add_item(TTR("Automatically Replace Tiles with Proxies")); advanced_menu_button->get_popup()->connect("id_pressed", callable_mp(this, &TileMapEditor::_advanced_menu_button_id_pressed)); - tilemap_toolbar->add_child(advanced_menu_button); + tile_map_toolbar->add_child(advanced_menu_button); missing_tileset_label = memnew(Label); missing_tileset_label->set_text(TTR("The edited TileMap node has no TileSet resource.")); @@ -3561,6 +3849,9 @@ TileMapEditor::TileMapEditor() { } _tab_changed(0); + + // Registers UndoRedo inspector callback. + EditorNode::get_singleton()->get_editor_data().add_undo_redo_inspector_hook_callback(callable_mp(this, &TileMapEditor::_undo_redo_inspector_callback)); } TileMapEditor::~TileMapEditor() { |