1/**************************************************************************/
2/* tile_map_editor.cpp */
3/**************************************************************************/
4/* This file is part of: */
5/* GODOT ENGINE */
6/* https://godotengine.org */
7/**************************************************************************/
8/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10/* */
11/* Permission is hereby granted, free of charge, to any person obtaining */
12/* a copy of this software and associated documentation files (the */
13/* "Software"), to deal in the Software without restriction, including */
14/* without limitation the rights to use, copy, modify, merge, publish, */
15/* distribute, sublicense, and/or sell copies of the Software, and to */
16/* permit persons to whom the Software is furnished to do so, subject to */
17/* the following conditions: */
18/* */
19/* The above copyright notice and this permission notice shall be */
20/* included in all copies or substantial portions of the Software. */
21/* */
22/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29/**************************************************************************/
30
31#include "tile_map_editor.h"
32
33#include "tiles_editor_plugin.h"
34
35#include "editor/editor_node.h"
36#include "editor/editor_resource_preview.h"
37#include "editor/editor_scale.h"
38#include "editor/editor_settings.h"
39#include "editor/editor_undo_redo_manager.h"
40#include "editor/plugins/canvas_item_editor_plugin.h"
41
42#include "scene/2d/camera_2d.h"
43#include "scene/gui/center_container.h"
44#include "scene/gui/split_container.h"
45
46#include "core/input/input.h"
47#include "core/math/geometry_2d.h"
48#include "core/os/keyboard.h"
49
50void TileMapEditorTilesPlugin::tile_set_changed() {
51 _update_fix_selected_and_hovered();
52 _update_tile_set_sources_list();
53 _update_source_display();
54 _update_patterns_list();
55}
56
57void TileMapEditorTilesPlugin::_on_random_tile_checkbox_toggled(bool p_pressed) {
58 scatter_controls_container->set_visible(p_pressed);
59}
60
61void TileMapEditorTilesPlugin::_on_scattering_spinbox_changed(double p_value) {
62 scattering = p_value;
63}
64
65void TileMapEditorTilesPlugin::_update_toolbar() {
66 // Stop draggig if needed.
67 _stop_dragging();
68
69 // Hide all settings.
70 for (int i = 0; i < tools_settings->get_child_count(); i++) {
71 Object::cast_to<CanvasItem>(tools_settings->get_child(i))->hide();
72 }
73
74 // Show only the correct settings.
75 if (tool_buttons_group->get_pressed_button() == select_tool_button) {
76 transform_toolbar->show();
77 } else if (tool_buttons_group->get_pressed_button() != bucket_tool_button) {
78 tools_settings_vsep->show();
79 picker_button->show();
80 erase_button->show();
81 transform_toolbar->show();
82 tools_settings_vsep_2->show();
83 random_tile_toggle->show();
84 scatter_label->show();
85 scatter_spinbox->show();
86 } else {
87 tools_settings_vsep->show();
88 picker_button->show();
89 erase_button->show();
90 transform_toolbar->show();
91 tools_settings_vsep_2->show();
92 bucket_contiguous_checkbox->show();
93 random_tile_toggle->show();
94 scatter_label->show();
95 scatter_spinbox->show();
96 }
97}
98
99void TileMapEditorTilesPlugin::_update_transform_buttons() {
100 TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
101 if (!tile_map) {
102 return;
103 }
104
105 Ref<TileSet> tile_set = tile_map->get_tileset();
106 if (tile_set.is_null() || selection_pattern.is_null()) {
107 return;
108 }
109
110 if (tile_set->get_tile_shape() == TileSet::TILE_SHAPE_SQUARE || selection_pattern->get_size() == Vector2i(1, 1)) {
111 transform_button_rotate_left->set_disabled(false);
112 transform_button_rotate_left->set_tooltip_text("");
113 transform_button_rotate_right->set_disabled(false);
114 transform_button_rotate_right->set_tooltip_text("");
115 } else {
116 const String tooltip_text = TTR("Can't rotate patterns when using non-square tile grid.");
117 transform_button_rotate_left->set_disabled(true);
118 transform_button_rotate_left->set_tooltip_text(tooltip_text);
119 transform_button_rotate_right->set_disabled(true);
120 transform_button_rotate_right->set_tooltip_text(tooltip_text);
121 }
122}
123
124Vector<TileMapSubEditorPlugin::TabData> TileMapEditorTilesPlugin::get_tabs() const {
125 Vector<TileMapSubEditorPlugin::TabData> tabs;
126 tabs.push_back({ toolbar, tiles_bottom_panel });
127 tabs.push_back({ toolbar, patterns_bottom_panel });
128 return tabs;
129}
130
131void TileMapEditorTilesPlugin::_tab_changed() {
132 if (tiles_bottom_panel->is_visible_in_tree()) {
133 _update_selection_pattern_from_tileset_tiles_selection();
134 } else if (patterns_bottom_panel->is_visible_in_tree()) {
135 _update_selection_pattern_from_tileset_pattern_selection();
136 }
137}
138
139void TileMapEditorTilesPlugin::_update_tile_set_sources_list() {
140 // Update the sources.
141 int old_current = sources_list->get_current();
142 int old_source = -1;
143 if (old_current > -1) {
144 old_source = sources_list->get_item_metadata(old_current);
145 }
146 sources_list->clear();
147
148 TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
149 if (!tile_map) {
150 return;
151 }
152
153 Ref<TileSet> tile_set = tile_map->get_tileset();
154 if (!tile_set.is_valid()) {
155 return;
156 }
157
158 if (!tile_set->has_source(old_source)) {
159 old_source = -1;
160 }
161
162 List<int> source_ids = TilesEditorUtils::get_singleton()->get_sorted_sources(tile_set);
163 for (const int &source_id : source_ids) {
164 TileSetSource *source = *tile_set->get_source(source_id);
165
166 Ref<Texture2D> texture;
167 String item_text;
168
169 // Common to all type of sources.
170 if (!source->get_name().is_empty()) {
171 item_text = source->get_name();
172 }
173
174 // Atlas source.
175 TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
176 if (atlas_source) {
177 texture = atlas_source->get_texture();
178 if (item_text.is_empty()) {
179 if (texture.is_valid()) {
180 item_text = texture->get_path().get_file();
181 } else {
182 item_text = vformat(TTR("No Texture Atlas Source (ID: %d)"), source_id);
183 }
184 }
185 }
186
187 // Scene collection source.
188 TileSetScenesCollectionSource *scene_collection_source = Object::cast_to<TileSetScenesCollectionSource>(source);
189 if (scene_collection_source) {
190 texture = tiles_bottom_panel->get_editor_theme_icon(SNAME("PackedScene"));
191 if (item_text.is_empty()) {
192 if (scene_collection_source->get_scene_tiles_count() > 0) {
193 item_text = vformat(TTR("Scene Collection Source (ID: %d)"), source_id);
194 } else {
195 item_text = vformat(TTR("Empty Scene Collection Source (ID: %d)"), source_id);
196 }
197 }
198 }
199
200 // Use default if not valid.
201 if (item_text.is_empty()) {
202 item_text = vformat(TTR("Unknown Type Source (ID: %d)"), source_id);
203 }
204 if (!texture.is_valid()) {
205 texture = missing_atlas_texture_icon;
206 }
207
208 sources_list->add_item(item_text, texture);
209 sources_list->set_item_metadata(-1, source_id);
210 }
211
212 if (sources_list->get_item_count() > 0) {
213 if (old_source >= 0) {
214 for (int i = 0; i < sources_list->get_item_count(); i++) {
215 if ((int)sources_list->get_item_metadata(i) == old_source) {
216 sources_list->set_current(i);
217 sources_list->ensure_current_is_visible();
218 break;
219 }
220 }
221 } else {
222 sources_list->set_current(0);
223 }
224 sources_list->emit_signal(SNAME("item_selected"), sources_list->get_current());
225 }
226
227 // Synchronize the lists.
228 TilesEditorUtils::get_singleton()->set_sources_lists_current(sources_list->get_current());
229}
230
231void TileMapEditorTilesPlugin::_update_source_display() {
232 // Update the atlas display.
233 TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
234 if (!tile_map) {
235 return;
236 }
237
238 Ref<TileSet> tile_set = tile_map->get_tileset();
239 if (!tile_set.is_valid()) {
240 return;
241 }
242
243 int source_index = sources_list->get_current();
244 if (source_index >= 0 && source_index < sources_list->get_item_count()) {
245 atlas_sources_split_container->show();
246 missing_source_label->hide();
247
248 int source_id = sources_list->get_item_metadata(source_index);
249 TileSetSource *source = *tile_set->get_source(source_id);
250 TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
251 TileSetScenesCollectionSource *scenes_collection_source = Object::cast_to<TileSetScenesCollectionSource>(source);
252
253 if (atlas_source) {
254 tile_atlas_view->show();
255 scene_tiles_list->hide();
256 invalid_source_label->hide();
257 _update_atlas_view();
258 } else if (scenes_collection_source) {
259 tile_atlas_view->hide();
260 scene_tiles_list->show();
261 invalid_source_label->hide();
262 _update_scenes_collection_view();
263 } else {
264 tile_atlas_view->hide();
265 scene_tiles_list->hide();
266 invalid_source_label->show();
267 }
268 } else {
269 atlas_sources_split_container->hide();
270 missing_source_label->show();
271
272 tile_atlas_view->hide();
273 scene_tiles_list->hide();
274 invalid_source_label->hide();
275 }
276}
277
278void TileMapEditorTilesPlugin::_patterns_item_list_gui_input(const Ref<InputEvent> &p_event) {
279 TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
280 if (!tile_map) {
281 return;
282 }
283
284 Ref<TileSet> tile_set = tile_map->get_tileset();
285 if (!tile_set.is_valid() || EditorNode::get_singleton()->is_resource_read_only(tile_set)) {
286 return;
287 }
288
289 if (ED_IS_SHORTCUT("tiles_editor/paste", p_event) && p_event->is_pressed() && !p_event->is_echo()) {
290 select_last_pattern = true;
291 int new_pattern_index = tile_set->get_patterns_count();
292 EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
293 undo_redo->create_action(TTR("Add TileSet pattern"));
294 undo_redo->add_do_method(*tile_set, "add_pattern", tile_map_clipboard, new_pattern_index);
295 undo_redo->add_undo_method(*tile_set, "remove_pattern", new_pattern_index);
296 undo_redo->commit_action();
297 patterns_item_list->accept_event();
298 }
299
300 if (ED_IS_SHORTCUT("tiles_editor/delete", p_event) && p_event->is_pressed() && !p_event->is_echo()) {
301 Vector<int> selected = patterns_item_list->get_selected_items();
302 EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
303 undo_redo->create_action(TTR("Remove TileSet patterns"));
304 for (int i = 0; i < selected.size(); i++) {
305 int pattern_index = selected[i];
306 undo_redo->add_do_method(*tile_set, "remove_pattern", pattern_index);
307 undo_redo->add_undo_method(*tile_set, "add_pattern", tile_set->get_pattern(pattern_index), pattern_index);
308 }
309 undo_redo->commit_action();
310 patterns_item_list->accept_event();
311 }
312}
313
314void TileMapEditorTilesPlugin::_pattern_preview_done(Ref<TileMapPattern> p_pattern, Ref<Texture2D> p_texture) {
315 // TODO optimize ?
316 for (int i = 0; i < patterns_item_list->get_item_count(); i++) {
317 if (patterns_item_list->get_item_metadata(i) == p_pattern) {
318 patterns_item_list->set_item_icon(i, p_texture);
319 break;
320 }
321 }
322}
323
324void TileMapEditorTilesPlugin::_update_patterns_list() {
325 TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
326 if (!tile_map) {
327 return;
328 }
329
330 Ref<TileSet> tile_set = tile_map->get_tileset();
331 if (!tile_set.is_valid()) {
332 return;
333 }
334
335 // Recreate the items.
336 patterns_item_list->clear();
337 for (int i = 0; i < tile_set->get_patterns_count(); i++) {
338 int id = patterns_item_list->add_item("");
339 patterns_item_list->set_item_metadata(id, tile_set->get_pattern(i));
340 patterns_item_list->set_item_tooltip(id, vformat(TTR("Index: %d"), i));
341 TilesEditorUtils::get_singleton()->queue_pattern_preview(tile_set, tile_set->get_pattern(i), callable_mp(this, &TileMapEditorTilesPlugin::_pattern_preview_done));
342 }
343
344 // Update the label visibility.
345 patterns_help_label->set_visible(patterns_item_list->get_item_count() == 0);
346
347 // Added a new pattern, thus select the last one.
348 if (select_last_pattern) {
349 patterns_item_list->select(tile_set->get_patterns_count() - 1);
350 patterns_item_list->grab_focus();
351 _update_selection_pattern_from_tileset_pattern_selection();
352 }
353 select_last_pattern = false;
354}
355
356void TileMapEditorTilesPlugin::_update_atlas_view() {
357 TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
358 if (!tile_map) {
359 return;
360 }
361
362 Ref<TileSet> tile_set = tile_map->get_tileset();
363 if (!tile_set.is_valid()) {
364 return;
365 }
366
367 int source_id = sources_list->get_item_metadata(sources_list->get_current());
368 TileSetSource *source = *tile_set->get_source(source_id);
369 TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
370 ERR_FAIL_NULL(atlas_source);
371
372 tile_atlas_view->set_atlas_source(*tile_map->get_tileset(), atlas_source, source_id);
373 TilesEditorUtils::get_singleton()->synchronize_atlas_view(tile_atlas_view);
374 tile_atlas_control->queue_redraw();
375}
376
377void TileMapEditorTilesPlugin::_update_scenes_collection_view() {
378 TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
379 if (!tile_map) {
380 return;
381 }
382
383 Ref<TileSet> tile_set = tile_map->get_tileset();
384 if (!tile_set.is_valid()) {
385 return;
386 }
387
388 int source_id = sources_list->get_item_metadata(sources_list->get_current());
389 TileSetSource *source = *tile_set->get_source(source_id);
390 TileSetScenesCollectionSource *scenes_collection_source = Object::cast_to<TileSetScenesCollectionSource>(source);
391 ERR_FAIL_NULL(scenes_collection_source);
392
393 // Clear the list.
394 scene_tiles_list->clear();
395
396 // Rebuild the list.
397 for (int i = 0; i < scenes_collection_source->get_scene_tiles_count(); i++) {
398 int scene_id = scenes_collection_source->get_scene_tile_id(i);
399
400 Ref<PackedScene> scene = scenes_collection_source->get_scene_tile_scene(scene_id);
401
402 int item_index = 0;
403 if (scene.is_valid()) {
404 item_index = scene_tiles_list->add_item(vformat("%s (Path: %s, ID: %d)", scene->get_path().get_file().get_basename(), scene->get_path(), scene_id));
405 Variant udata = i;
406 EditorResourcePreview::get_singleton()->queue_edited_resource_preview(scene, this, "_scene_thumbnail_done", udata);
407 } else {
408 item_index = scene_tiles_list->add_item(TTR("Tile with Invalid Scene"), tiles_bottom_panel->get_editor_theme_icon(SNAME("PackedScene")));
409 }
410 scene_tiles_list->set_item_metadata(item_index, scene_id);
411
412 // Check if in selection.
413 if (tile_set_selection.has(TileMapCell(source_id, Vector2i(), scene_id))) {
414 scene_tiles_list->select(item_index, false);
415 }
416 }
417 if (scene_tiles_list->get_item_count() == 0) {
418 scene_tiles_list->add_item(TTR("The selected scene collection source has no scenes. Add scenes in the TileSet bottom tab."));
419 scene_tiles_list->set_item_disabled(-1, true);
420 }
421
422 // Icon size update.
423 int int_size = int(EDITOR_GET("filesystem/file_dialog/thumbnail_size")) * EDSCALE;
424 scene_tiles_list->set_fixed_icon_size(Vector2(int_size, int_size));
425}
426
427void TileMapEditorTilesPlugin::_scene_thumbnail_done(const String &p_path, const Ref<Texture2D> &p_preview, const Ref<Texture2D> &p_small_preview, Variant p_ud) {
428 int index = p_ud;
429
430 if (index >= 0 && index < scene_tiles_list->get_item_count()) {
431 scene_tiles_list->set_item_icon(index, p_preview);
432 }
433}
434
435void TileMapEditorTilesPlugin::_scenes_list_multi_selected(int p_index, bool p_selected) {
436 TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
437 if (!tile_map) {
438 return;
439 }
440
441 Ref<TileSet> tile_set = tile_map->get_tileset();
442 if (!tile_set.is_valid()) {
443 return;
444 }
445
446 // Add or remove the Tile form the selection.
447 int scene_id = scene_tiles_list->get_item_metadata(p_index);
448 int source_id = sources_list->get_item_metadata(sources_list->get_current());
449 TileSetSource *source = *tile_set->get_source(source_id);
450 TileSetScenesCollectionSource *scenes_collection_source = Object::cast_to<TileSetScenesCollectionSource>(source);
451 ERR_FAIL_NULL(scenes_collection_source);
452
453 TileMapCell selected = TileMapCell(source_id, Vector2i(), scene_id);
454
455 // Clear the selection if shift is not pressed.
456 if (!Input::get_singleton()->is_key_pressed(Key::SHIFT)) {
457 tile_set_selection.clear();
458 }
459
460 if (p_selected) {
461 tile_set_selection.insert(selected);
462 } else {
463 if (tile_set_selection.has(selected)) {
464 tile_set_selection.erase(selected);
465 }
466 }
467
468 _update_selection_pattern_from_tileset_tiles_selection();
469}
470
471void TileMapEditorTilesPlugin::_scenes_list_lmb_empty_clicked(const Vector2 &p_pos, MouseButton p_mouse_button_index) {
472 if (p_mouse_button_index != MouseButton::LEFT) {
473 return;
474 }
475
476 scene_tiles_list->deselect_all();
477 tile_set_selection.clear();
478 tile_map_selection.clear();
479 selection_pattern.instantiate();
480 _update_selection_pattern_from_tileset_tiles_selection();
481}
482
483void TileMapEditorTilesPlugin::_update_theme() {
484 source_sort_button->set_icon(tiles_bottom_panel->get_editor_theme_icon(SNAME("Sort")));
485 select_tool_button->set_icon(tiles_bottom_panel->get_editor_theme_icon(SNAME("ToolSelect")));
486 paint_tool_button->set_icon(tiles_bottom_panel->get_editor_theme_icon(SNAME("Edit")));
487 line_tool_button->set_icon(tiles_bottom_panel->get_editor_theme_icon(SNAME("Line")));
488 rect_tool_button->set_icon(tiles_bottom_panel->get_editor_theme_icon(SNAME("Rectangle")));
489 bucket_tool_button->set_icon(tiles_bottom_panel->get_editor_theme_icon(SNAME("Bucket")));
490
491 picker_button->set_icon(tiles_bottom_panel->get_editor_theme_icon(SNAME("ColorPick")));
492 erase_button->set_icon(tiles_bottom_panel->get_editor_theme_icon(SNAME("Eraser")));
493 random_tile_toggle->set_icon(tiles_bottom_panel->get_editor_theme_icon(SNAME("RandomNumberGenerator")));
494
495 transform_button_rotate_left->set_icon(tiles_bottom_panel->get_editor_theme_icon("RotateLeft"));
496 transform_button_rotate_right->set_icon(tiles_bottom_panel->get_editor_theme_icon("RotateRight"));
497 transform_button_flip_h->set_icon(tiles_bottom_panel->get_editor_theme_icon("MirrorX"));
498 transform_button_flip_v->set_icon(tiles_bottom_panel->get_editor_theme_icon("MirrorY"));
499
500 missing_atlas_texture_icon = tiles_bottom_panel->get_editor_theme_icon(SNAME("TileSet"));
501 _update_tile_set_sources_list();
502}
503
504bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p_event) {
505 if (!(tiles_bottom_panel->is_visible_in_tree() || patterns_bottom_panel->is_visible_in_tree())) {
506 // If the bottom editor is not visible, we ignore inputs.
507 return false;
508 }
509
510 if (CanvasItemEditor::get_singleton()->get_current_tool() != CanvasItemEditor::TOOL_SELECT) {
511 return false;
512 }
513
514 TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
515 if (!tile_map) {
516 return false;
517 }
518
519 if (tile_map_layer < 0) {
520 return false;
521 }
522 ERR_FAIL_INDEX_V(tile_map_layer, tile_map->get_layers_count(), false);
523
524 Ref<TileSet> tile_set = tile_map->get_tileset();
525 if (!tile_set.is_valid()) {
526 return false;
527 }
528
529 // Shortcuts
530 if (ED_IS_SHORTCUT("tiles_editor/cut", p_event) || ED_IS_SHORTCUT("tiles_editor/copy", p_event)) {
531 // Fill in the clipboard.
532 if (!tile_map_selection.is_empty()) {
533 tile_map_clipboard.instantiate();
534 TypedArray<Vector2i> coords_array;
535 for (const Vector2i &E : tile_map_selection) {
536 coords_array.push_back(E);
537 }
538 tile_map_clipboard = tile_map->get_pattern(tile_map_layer, coords_array);
539 }
540
541 if (ED_IS_SHORTCUT("tiles_editor/cut", p_event)) {
542 // Delete selected tiles.
543 if (!tile_map_selection.is_empty()) {
544 EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
545 undo_redo->create_action(TTR("Delete tiles"));
546 for (const Vector2i &E : tile_map_selection) {
547 undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, E, TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE);
548 undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, E, tile_map->get_cell_source_id(tile_map_layer, E), tile_map->get_cell_atlas_coords(tile_map_layer, E), tile_map->get_cell_alternative_tile(tile_map_layer, E));
549 }
550 undo_redo->add_undo_method(this, "_set_tile_map_selection", _get_tile_map_selection());
551 tile_map_selection.clear();
552 undo_redo->add_do_method(this, "_set_tile_map_selection", _get_tile_map_selection());
553 undo_redo->commit_action();
554 }
555 }
556
557 return true;
558 }
559 if (ED_IS_SHORTCUT("tiles_editor/paste", p_event)) {
560 if (drag_type == DRAG_TYPE_NONE) {
561 drag_type = DRAG_TYPE_CLIPBOARD_PASTE;
562 }
563 CanvasItemEditor::get_singleton()->update_viewport();
564 return true;
565 }
566 if (ED_IS_SHORTCUT("tiles_editor/cancel", p_event)) {
567 if (drag_type == DRAG_TYPE_CLIPBOARD_PASTE) {
568 drag_type = DRAG_TYPE_NONE;
569 CanvasItemEditor::get_singleton()->update_viewport();
570 return true;
571 }
572 }
573 if (ED_IS_SHORTCUT("tiles_editor/delete", p_event)) {
574 // Delete selected tiles.
575 if (!tile_map_selection.is_empty()) {
576 EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
577 undo_redo->create_action(TTR("Delete tiles"));
578 for (const Vector2i &E : tile_map_selection) {
579 undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, E, TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE);
580 undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, E, tile_map->get_cell_source_id(tile_map_layer, E), tile_map->get_cell_atlas_coords(tile_map_layer, E), tile_map->get_cell_alternative_tile(tile_map_layer, E));
581 }
582 undo_redo->add_undo_method(this, "_set_tile_map_selection", _get_tile_map_selection());
583 tile_map_selection.clear();
584 undo_redo->add_do_method(this, "_set_tile_map_selection", _get_tile_map_selection());
585 undo_redo->commit_action();
586 }
587 return true;
588 }
589
590 Ref<InputEventKey> k = p_event;
591 if (k.is_valid() && k->is_pressed() && !k->is_echo()) {
592 for (BaseButton *b : viewport_shortcut_buttons) {
593 if (b->is_disabled()) {
594 continue;
595 }
596
597 if (b->get_shortcut().is_valid() && b->get_shortcut()->matches_event(p_event)) {
598 if (b->is_toggle_mode()) {
599 b->set_pressed(b->get_button_group().is_valid() || !b->is_pressed());
600 } else {
601 // Can't press a button without toggle mode, so just emit the signal directly.
602 b->emit_signal(SNAME("pressed"));
603 }
604 return true;
605 }
606 }
607 }
608
609 Ref<InputEventMouseMotion> mm = p_event;
610 if (mm.is_valid()) {
611 has_mouse = true;
612 Transform2D xform = CanvasItemEditor::get_singleton()->get_canvas_transform() * tile_map->get_global_transform_with_canvas();
613 Vector2 mpos = xform.affine_inverse().xform(mm->get_position());
614
615 switch (drag_type) {
616 case DRAG_TYPE_PAINT: {
617 HashMap<Vector2i, TileMapCell> to_draw = _draw_line(drag_start_mouse_pos, drag_last_mouse_pos, mpos, drag_erasing);
618 for (const KeyValue<Vector2i, TileMapCell> &E : to_draw) {
619 Vector2i coords = E.key;
620 if (!drag_modified.has(coords)) {
621 drag_modified.insert(coords, tile_map->get_cell(tile_map_layer, coords));
622 if (!drag_erasing && E.value.source_id == TileSet::INVALID_SOURCE) {
623 continue;
624 }
625 tile_map->set_cell(tile_map_layer, coords, E.value.source_id, E.value.get_atlas_coords(), E.value.alternative_tile);
626 }
627 }
628 _fix_invalid_tiles_in_tile_map_selection();
629 } break;
630 case DRAG_TYPE_BUCKET: {
631 Vector<Vector2i> line = TileMapEditor::get_line(tile_map, tile_map->local_to_map(drag_last_mouse_pos), tile_map->local_to_map(mpos));
632 for (int i = 0; i < line.size(); i++) {
633 if (!drag_modified.has(line[i])) {
634 HashMap<Vector2i, TileMapCell> to_draw = _draw_bucket_fill(line[i], bucket_contiguous_checkbox->is_pressed(), drag_erasing);
635 for (const KeyValue<Vector2i, TileMapCell> &E : to_draw) {
636 Vector2i coords = E.key;
637 if (!drag_modified.has(coords)) {
638 drag_modified.insert(coords, tile_map->get_cell(tile_map_layer, coords));
639 if (!drag_erasing && E.value.source_id == TileSet::INVALID_SOURCE) {
640 continue;
641 }
642 tile_map->set_cell(tile_map_layer, coords, E.value.source_id, E.value.get_atlas_coords(), E.value.alternative_tile);
643 }
644 }
645 }
646 }
647 _fix_invalid_tiles_in_tile_map_selection();
648 } break;
649 default:
650 break;
651 }
652 drag_last_mouse_pos = mpos;
653 CanvasItemEditor::get_singleton()->update_viewport();
654
655 return true;
656 }
657
658 Ref<InputEventMouseButton> mb = p_event;
659 if (mb.is_valid()) {
660 has_mouse = true;
661 Transform2D xform = CanvasItemEditor::get_singleton()->get_canvas_transform() * tile_map->get_global_transform_with_canvas();
662 Vector2 mpos = xform.affine_inverse().xform(mb->get_position());
663
664 if (mb->get_button_index() == MouseButton::LEFT || mb->get_button_index() == MouseButton::RIGHT) {
665 if (mb->is_pressed()) {
666 // Pressed
667 if (erase_button->is_pressed() || mb->get_button_index() == MouseButton::RIGHT) {
668 drag_erasing = true;
669 }
670
671 if (drag_type == DRAG_TYPE_CLIPBOARD_PASTE) {
672 // Cancel tile pasting on right-click
673 if (mb->get_button_index() == MouseButton::RIGHT) {
674 drag_type = DRAG_TYPE_NONE;
675 }
676 } else if (tool_buttons_group->get_pressed_button() == select_tool_button) {
677 drag_start_mouse_pos = mpos;
678 if (tile_map_selection.has(tile_map->local_to_map(drag_start_mouse_pos)) && !mb->is_shift_pressed()) {
679 // Move the selection
680 _update_selection_pattern_from_tilemap_selection(); // Make sure the pattern is up to date before moving.
681 drag_type = DRAG_TYPE_MOVE;
682 drag_modified.clear();
683 for (const Vector2i &E : tile_map_selection) {
684 Vector2i coords = E;
685 drag_modified.insert(coords, tile_map->get_cell(tile_map_layer, coords));
686 tile_map->set_cell(tile_map_layer, coords, TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE);
687 }
688 } else {
689 // Select tiles
690 drag_type = DRAG_TYPE_SELECT;
691 }
692 } else {
693 // Check if we are picking a tile.
694 if (picker_button->is_pressed() || (Input::get_singleton()->is_key_pressed(Key::CMD_OR_CTRL) && !Input::get_singleton()->is_key_pressed(Key::SHIFT))) {
695 drag_type = DRAG_TYPE_PICK;
696 drag_start_mouse_pos = mpos;
697 } else {
698 // Paint otherwise.
699 if (tool_buttons_group->get_pressed_button() == paint_tool_button && !Input::get_singleton()->is_key_pressed(Key::CMD_OR_CTRL) && !Input::get_singleton()->is_key_pressed(Key::SHIFT)) {
700 drag_type = DRAG_TYPE_PAINT;
701 drag_start_mouse_pos = mpos;
702 drag_modified.clear();
703 HashMap<Vector2i, TileMapCell> to_draw = _draw_line(drag_start_mouse_pos, mpos, mpos, drag_erasing);
704 for (const KeyValue<Vector2i, TileMapCell> &E : to_draw) {
705 if (!drag_erasing && E.value.source_id == TileSet::INVALID_SOURCE) {
706 continue;
707 }
708 Vector2i coords = E.key;
709 if (!drag_modified.has(coords)) {
710 drag_modified.insert(coords, tile_map->get_cell(tile_map_layer, coords));
711 }
712 tile_map->set_cell(tile_map_layer, coords, E.value.source_id, E.value.get_atlas_coords(), E.value.alternative_tile);
713 }
714 _fix_invalid_tiles_in_tile_map_selection();
715 } else if (tool_buttons_group->get_pressed_button() == line_tool_button || (tool_buttons_group->get_pressed_button() == paint_tool_button && Input::get_singleton()->is_key_pressed(Key::SHIFT) && !Input::get_singleton()->is_key_pressed(Key::CMD_OR_CTRL))) {
716 drag_type = DRAG_TYPE_LINE;
717 drag_start_mouse_pos = mpos;
718 drag_modified.clear();
719 } else if (tool_buttons_group->get_pressed_button() == rect_tool_button || (tool_buttons_group->get_pressed_button() == paint_tool_button && Input::get_singleton()->is_key_pressed(Key::SHIFT) && Input::get_singleton()->is_key_pressed(Key::CMD_OR_CTRL))) {
720 drag_type = DRAG_TYPE_RECT;
721 drag_start_mouse_pos = mpos;
722 drag_modified.clear();
723 } else if (tool_buttons_group->get_pressed_button() == bucket_tool_button) {
724 drag_type = DRAG_TYPE_BUCKET;
725 drag_start_mouse_pos = mpos;
726 drag_modified.clear();
727 Vector<Vector2i> line = TileMapEditor::get_line(tile_map, tile_map->local_to_map(drag_last_mouse_pos), tile_map->local_to_map(mpos));
728 for (int i = 0; i < line.size(); i++) {
729 if (!drag_modified.has(line[i])) {
730 HashMap<Vector2i, TileMapCell> to_draw = _draw_bucket_fill(line[i], bucket_contiguous_checkbox->is_pressed(), drag_erasing);
731 for (const KeyValue<Vector2i, TileMapCell> &E : to_draw) {
732 Vector2i coords = E.key;
733 if (!drag_modified.has(coords)) {
734 drag_modified.insert(coords, tile_map->get_cell(tile_map_layer, coords));
735 if (!drag_erasing && E.value.source_id == TileSet::INVALID_SOURCE) {
736 continue;
737 }
738 tile_map->set_cell(tile_map_layer, coords, E.value.source_id, E.value.get_atlas_coords(), E.value.alternative_tile);
739 }
740 }
741 }
742 }
743 _fix_invalid_tiles_in_tile_map_selection();
744 }
745 }
746 }
747
748 } else {
749 // Released
750 _stop_dragging();
751 drag_erasing = false;
752 }
753
754 CanvasItemEditor::get_singleton()->update_viewport();
755
756 return true;
757 }
758 drag_last_mouse_pos = mpos;
759 }
760
761 return false;
762}
763
764void TileMapEditorTilesPlugin::forward_canvas_draw_over_viewport(Control *p_overlay) {
765 TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
766 if (!tile_map) {
767 return;
768 }
769
770 if (tile_map_layer < 0) {
771 return;
772 }
773 ERR_FAIL_INDEX(tile_map_layer, tile_map->get_layers_count());
774
775 Ref<TileSet> tile_set = tile_map->get_tileset();
776 if (!tile_set.is_valid()) {
777 return;
778 }
779
780 if (!tile_map->is_visible_in_tree()) {
781 return;
782 }
783
784 Transform2D xform = CanvasItemEditor::get_singleton()->get_canvas_transform() * tile_map->get_global_transform_with_canvas();
785 Vector2 mpos = tile_map->get_local_mouse_position();
786 Vector2i tile_shape_size = tile_set->get_tile_size();
787
788 // Draw the selection.
789 if ((tiles_bottom_panel->is_visible_in_tree() || patterns_bottom_panel->is_visible_in_tree()) && tool_buttons_group->get_pressed_button() == select_tool_button) {
790 // In select mode, we only draw the current selection if we are modifying it (pressing control or shift).
791 if (drag_type == DRAG_TYPE_MOVE || (drag_type == DRAG_TYPE_SELECT && !Input::get_singleton()->is_key_pressed(Key::CMD_OR_CTRL) && !Input::get_singleton()->is_key_pressed(Key::SHIFT))) {
792 // Do nothing.
793 } else {
794 Color grid_color = EDITOR_GET("editors/tiles_editor/grid_color");
795 Color selection_color = Color().from_hsv(Math::fposmod(grid_color.get_h() + 0.5, 1.0), grid_color.get_s(), grid_color.get_v(), 1.0);
796 tile_map->draw_cells_outline(p_overlay, tile_map_selection, selection_color, xform);
797 }
798 }
799
800 // Handle the preview of the tiles to be placed.
801 if ((tiles_bottom_panel->is_visible_in_tree() || patterns_bottom_panel->is_visible_in_tree()) && CanvasItemEditor::get_singleton()->get_current_tool() == CanvasItemEditor::TOOL_SELECT && has_mouse) { // Only if the tilemap editor is opened and the viewport is hovered.
802 HashMap<Vector2i, TileMapCell> preview;
803 Rect2i drawn_grid_rect;
804
805 if (drag_type == DRAG_TYPE_PICK) {
806 // Draw the area being picked.
807 Rect2i rect = Rect2i(tile_map->local_to_map(drag_start_mouse_pos), tile_map->local_to_map(mpos) - tile_map->local_to_map(drag_start_mouse_pos)).abs();
808 rect.size += Vector2i(1, 1);
809 for (int x = rect.position.x; x < rect.get_end().x; x++) {
810 for (int y = rect.position.y; y < rect.get_end().y; y++) {
811 Vector2i coords = Vector2i(x, y);
812 if (tile_map->get_cell_source_id(tile_map_layer, coords) != TileSet::INVALID_SOURCE) {
813 Transform2D tile_xform(0, tile_shape_size, 0, tile_map->map_to_local(coords));
814 tile_set->draw_tile_shape(p_overlay, xform * tile_xform, Color(1.0, 1.0, 1.0), false);
815 }
816 }
817 }
818 } else if (drag_type == DRAG_TYPE_SELECT) {
819 // Draw the area being selected.
820 Rect2i rect = Rect2i(tile_map->local_to_map(drag_start_mouse_pos), tile_map->local_to_map(mpos) - tile_map->local_to_map(drag_start_mouse_pos)).abs();
821 rect.size += Vector2i(1, 1);
822 RBSet<Vector2i> to_draw;
823 for (int x = rect.position.x; x < rect.get_end().x; x++) {
824 for (int y = rect.position.y; y < rect.get_end().y; y++) {
825 Vector2i coords = Vector2i(x, y);
826 if (tile_map->get_cell_source_id(tile_map_layer, coords) != TileSet::INVALID_SOURCE) {
827 to_draw.insert(coords);
828 }
829 Transform2D tile_xform(0, tile_shape_size, 0, tile_map->map_to_local(coords));
830 tile_set->draw_tile_shape(p_overlay, xform * tile_xform, Color(1.0, 1.0, 1.0, 0.2), true);
831 }
832 }
833 tile_map->draw_cells_outline(p_overlay, to_draw, Color(1.0, 1.0, 1.0), xform);
834 } else if (drag_type == DRAG_TYPE_MOVE) {
835 if (!(patterns_item_list->is_visible_in_tree() && patterns_item_list->has_point(patterns_item_list->get_local_mouse_position()))) {
836 // Preview when moving.
837 Vector2i top_left;
838 if (!tile_map_selection.is_empty()) {
839 top_left = tile_map_selection.front()->get();
840 }
841 for (const Vector2i &E : tile_map_selection) {
842 top_left = top_left.min(E);
843 }
844 Vector2i offset = drag_start_mouse_pos - tile_map->map_to_local(top_left);
845 offset = tile_map->local_to_map(mpos - offset) - tile_map->local_to_map(drag_start_mouse_pos - offset);
846
847 TypedArray<Vector2i> selection_used_cells = selection_pattern->get_used_cells();
848 for (int i = 0; i < selection_used_cells.size(); i++) {
849 Vector2i coords = tile_map->map_pattern(offset + top_left, selection_used_cells[i], selection_pattern);
850 preview[coords] = TileMapCell(selection_pattern->get_cell_source_id(selection_used_cells[i]), selection_pattern->get_cell_atlas_coords(selection_used_cells[i]), selection_pattern->get_cell_alternative_tile(selection_used_cells[i]));
851 }
852 }
853 } else if (drag_type == DRAG_TYPE_CLIPBOARD_PASTE) {
854 // Preview when pasting.
855 Vector2 mouse_offset = (Vector2(tile_map_clipboard->get_size()) / 2.0 - Vector2(0.5, 0.5)) * tile_set->get_tile_size();
856 TypedArray<Vector2i> clipboard_used_cells = tile_map_clipboard->get_used_cells();
857 for (int i = 0; i < clipboard_used_cells.size(); i++) {
858 Vector2i coords = tile_map->map_pattern(tile_map->local_to_map(mpos - mouse_offset), clipboard_used_cells[i], tile_map_clipboard);
859 preview[coords] = TileMapCell(tile_map_clipboard->get_cell_source_id(clipboard_used_cells[i]), tile_map_clipboard->get_cell_atlas_coords(clipboard_used_cells[i]), tile_map_clipboard->get_cell_alternative_tile(clipboard_used_cells[i]));
860 }
861 } else if (!picker_button->is_pressed() && !(drag_type == DRAG_TYPE_NONE && Input::get_singleton()->is_key_pressed(Key::CMD_OR_CTRL) && !Input::get_singleton()->is_key_pressed(Key::SHIFT))) {
862 bool expand_grid = false;
863 if (tool_buttons_group->get_pressed_button() == paint_tool_button && drag_type == DRAG_TYPE_NONE) {
864 // Preview for a single pattern.
865 preview = _draw_line(mpos, mpos, mpos, erase_button->is_pressed());
866 expand_grid = true;
867 } else if (tool_buttons_group->get_pressed_button() == line_tool_button || drag_type == DRAG_TYPE_LINE) {
868 if (drag_type == DRAG_TYPE_NONE) {
869 // Preview for a single pattern.
870 preview = _draw_line(mpos, mpos, mpos, erase_button->is_pressed());
871 expand_grid = true;
872 } else if (drag_type == DRAG_TYPE_LINE) {
873 // Preview for a line pattern.
874 preview = _draw_line(drag_start_mouse_pos, drag_start_mouse_pos, mpos, drag_erasing);
875 expand_grid = true;
876 }
877 } else if (drag_type == DRAG_TYPE_RECT) {
878 // Preview for a rect pattern.
879 preview = _draw_rect(tile_map->local_to_map(drag_start_mouse_pos), tile_map->local_to_map(mpos), drag_erasing);
880 expand_grid = true;
881 } else if (tool_buttons_group->get_pressed_button() == bucket_tool_button && drag_type == DRAG_TYPE_NONE) {
882 // Preview for a fill pattern.
883 preview = _draw_bucket_fill(tile_map->local_to_map(mpos), bucket_contiguous_checkbox->is_pressed(), erase_button->is_pressed());
884 }
885
886 // Expand the grid if needed
887 if (expand_grid && !preview.is_empty()) {
888 drawn_grid_rect = Rect2i(preview.begin()->key, Vector2i(0, 0));
889 for (const KeyValue<Vector2i, TileMapCell> &E : preview) {
890 drawn_grid_rect.expand_to(E.key);
891 }
892 drawn_grid_rect.size += Vector2i(1, 1);
893 }
894 }
895
896 if (!preview.is_empty()) {
897 const int fading = 5;
898
899 // Draw the lines of the grid behind the preview.
900 bool display_grid = EDITOR_GET("editors/tiles_editor/display_grid");
901 if (display_grid) {
902 Color grid_color = EDITOR_GET("editors/tiles_editor/grid_color");
903 if (drawn_grid_rect.size.x > 0 && drawn_grid_rect.size.y > 0) {
904 drawn_grid_rect = drawn_grid_rect.grow(fading);
905 for (int x = drawn_grid_rect.position.x; x < (drawn_grid_rect.position.x + drawn_grid_rect.size.x); x++) {
906 for (int y = drawn_grid_rect.position.y; y < (drawn_grid_rect.position.y + drawn_grid_rect.size.y); y++) {
907 Vector2i pos_in_rect = Vector2i(x, y) - drawn_grid_rect.position;
908
909 // Fade out the border of the grid.
910 float left_opacity = CLAMP(Math::inverse_lerp(0.0f, (float)fading, (float)pos_in_rect.x), 0.0f, 1.0f);
911 float right_opacity = CLAMP(Math::inverse_lerp((float)drawn_grid_rect.size.x, (float)(drawn_grid_rect.size.x - fading), (float)(pos_in_rect.x + 1)), 0.0f, 1.0f);
912 float top_opacity = CLAMP(Math::inverse_lerp(0.0f, (float)fading, (float)pos_in_rect.y), 0.0f, 1.0f);
913 float bottom_opacity = CLAMP(Math::inverse_lerp((float)drawn_grid_rect.size.y, (float)(drawn_grid_rect.size.y - fading), (float)(pos_in_rect.y + 1)), 0.0f, 1.0f);
914 float opacity = CLAMP(MIN(left_opacity, MIN(right_opacity, MIN(top_opacity, bottom_opacity))) + 0.1, 0.0f, 1.0f);
915
916 Transform2D tile_xform;
917 tile_xform.set_origin(tile_map->map_to_local(Vector2(x, y)));
918 tile_xform.set_scale(tile_shape_size);
919 Color color = grid_color;
920 color.a = color.a * opacity;
921 tile_set->draw_tile_shape(p_overlay, xform * tile_xform, color, false);
922 }
923 }
924 }
925 }
926
927 // Draw the preview.
928 for (const KeyValue<Vector2i, TileMapCell> &E : preview) {
929 Transform2D tile_xform;
930 tile_xform.set_origin(tile_map->map_to_local(E.key));
931 tile_xform.set_scale(tile_set->get_tile_size());
932 if (!(drag_erasing || erase_button->is_pressed()) && random_tile_toggle->is_pressed()) {
933 tile_set->draw_tile_shape(p_overlay, xform * tile_xform, Color(1.0, 1.0, 1.0, 0.5), true);
934 } else {
935 if (tile_set->has_source(E.value.source_id)) {
936 TileSetSource *source = *tile_set->get_source(E.value.source_id);
937 TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
938 if (atlas_source) {
939 // Get tile data.
940 TileData *tile_data = atlas_source->get_tile_data(E.value.get_atlas_coords(), E.value.alternative_tile);
941 if (!tile_data) {
942 continue;
943 }
944
945 // Compute the offset
946 Rect2i source_rect = atlas_source->get_tile_texture_region(E.value.get_atlas_coords());
947 Vector2i tile_offset = tile_data->get_texture_origin();
948
949 // Compute the destination rectangle in the CanvasItem.
950 Rect2 dest_rect;
951 dest_rect.size = source_rect.size;
952
953 bool transpose = tile_data->get_transpose() ^ bool(E.value.alternative_tile & TileSetAtlasSource::TRANSFORM_TRANSPOSE);
954 if (transpose) {
955 dest_rect.position = (tile_map->map_to_local(E.key) - Vector2(dest_rect.size.y, dest_rect.size.x) / 2 - tile_offset);
956 } else {
957 dest_rect.position = (tile_map->map_to_local(E.key) - dest_rect.size / 2 - tile_offset);
958 }
959
960 if (tile_data->get_flip_h() ^ bool(E.value.alternative_tile & TileSetAtlasSource::TRANSFORM_FLIP_H)) {
961 dest_rect.size.x = -dest_rect.size.x;
962 }
963
964 if (tile_data->get_flip_v() ^ bool(E.value.alternative_tile & TileSetAtlasSource::TRANSFORM_FLIP_V)) {
965 dest_rect.size.y = -dest_rect.size.y;
966 }
967
968 // Get the tile modulation.
969 Color modulate = tile_data->get_modulate();
970 Color self_modulate = tile_map->get_modulate_in_tree() * tile_map->get_self_modulate();
971 modulate *= self_modulate;
972 modulate *= tile_map->get_layer_modulate(tile_map_layer);
973
974 // Draw the tile.
975 p_overlay->draw_set_transform_matrix(xform);
976 p_overlay->draw_texture_rect_region(atlas_source->get_texture(), dest_rect, source_rect, modulate * Color(1.0, 1.0, 1.0, 0.5), transpose, tile_set->is_uv_clipping());
977 p_overlay->draw_set_transform_matrix(Transform2D());
978 } else {
979 tile_set->draw_tile_shape(p_overlay, xform * tile_xform, Color(1.0, 1.0, 1.0, 0.5), true);
980 }
981 } else {
982 tile_set->draw_tile_shape(p_overlay, xform * tile_xform, Color(0.0, 0.0, 0.0, 0.5), true);
983 }
984 }
985 }
986 }
987
988 Ref<Font> font = p_overlay->get_theme_font(SNAME("font"), SNAME("Label"));
989 int font_size = p_overlay->get_theme_font_size(SNAME("font_size"), SNAME("Label"));
990 Point2 msgpos = Point2(20 * EDSCALE, p_overlay->get_size().y - 20 * EDSCALE);
991
992 String text = tile_map->local_to_map(tile_map->get_local_mouse_position());
993 if (drag_type == DRAG_TYPE_RECT) {
994 Vector2i size = tile_map->local_to_map(tile_map->get_local_mouse_position()) - tile_map->local_to_map(drag_start_mouse_pos);
995 text += vformat(" %s (%dx%d)", TTR("Drawing Rect:"), ABS(size.x) + 1, ABS(size.y) + 1);
996 }
997
998 p_overlay->draw_string(font, msgpos + Point2(1, 1), text, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, Color(0, 0, 0, 0.8));
999 p_overlay->draw_string(font, msgpos + Point2(-1, -1), text, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, Color(0, 0, 0, 0.8));
1000 p_overlay->draw_string(font, msgpos, text, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, Color(1, 1, 1, 1));
1001 }
1002}
1003
1004void TileMapEditorTilesPlugin::_mouse_exited_viewport() {
1005 has_mouse = false;
1006 CanvasItemEditor::get_singleton()->update_viewport();
1007}
1008
1009TileMapCell TileMapEditorTilesPlugin::_pick_random_tile(Ref<TileMapPattern> p_pattern) {
1010 TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
1011 if (!tile_map) {
1012 return TileMapCell();
1013 }
1014
1015 Ref<TileSet> tile_set = tile_map->get_tileset();
1016 if (!tile_set.is_valid()) {
1017 return TileMapCell();
1018 }
1019
1020 TypedArray<Vector2i> used_cells = p_pattern->get_used_cells();
1021 double sum = 0.0;
1022 for (int i = 0; i < used_cells.size(); i++) {
1023 int source_id = p_pattern->get_cell_source_id(used_cells[i]);
1024 Vector2i atlas_coords = p_pattern->get_cell_atlas_coords(used_cells[i]);
1025 int alternative_tile = p_pattern->get_cell_alternative_tile(used_cells[i]);
1026
1027 TileSetSource *source = *tile_set->get_source(source_id);
1028 TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
1029 if (atlas_source) {
1030 TileData *tile_data = atlas_source->get_tile_data(atlas_coords, alternative_tile);
1031 ERR_FAIL_NULL_V(tile_data, TileMapCell());
1032 sum += tile_data->get_probability();
1033 } else {
1034 sum += 1.0;
1035 }
1036 }
1037
1038 double empty_probability = sum * scattering;
1039 double current = 0.0;
1040 double rand = Math::random(0.0, sum + empty_probability);
1041 for (int i = 0; i < used_cells.size(); i++) {
1042 int source_id = p_pattern->get_cell_source_id(used_cells[i]);
1043 Vector2i atlas_coords = p_pattern->get_cell_atlas_coords(used_cells[i]);
1044 int alternative_tile = p_pattern->get_cell_alternative_tile(used_cells[i]);
1045
1046 TileSetSource *source = *tile_set->get_source(source_id);
1047 TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
1048 if (atlas_source) {
1049 current += atlas_source->get_tile_data(atlas_coords, alternative_tile)->get_probability();
1050 } else {
1051 current += 1.0;
1052 }
1053
1054 if (current >= rand) {
1055 return TileMapCell(source_id, atlas_coords, alternative_tile);
1056 }
1057 }
1058 return TileMapCell();
1059}
1060
1061HashMap<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_line(Vector2 p_start_drag_mouse_pos, Vector2 p_from_mouse_pos, Vector2 p_to_mouse_pos, bool p_erase) {
1062 TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
1063 if (!tile_map) {
1064 return HashMap<Vector2i, TileMapCell>();
1065 }
1066
1067 Ref<TileSet> tile_set = tile_map->get_tileset();
1068 if (!tile_set.is_valid()) {
1069 return HashMap<Vector2i, TileMapCell>();
1070 }
1071
1072 // Get or create the pattern.
1073 Ref<TileMapPattern> erase_pattern;
1074 erase_pattern.instantiate();
1075 erase_pattern->set_cell(Vector2i(0, 0), TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE);
1076 Ref<TileMapPattern> pattern = p_erase ? erase_pattern : selection_pattern;
1077
1078 HashMap<Vector2i, TileMapCell> output;
1079 if (!pattern->is_empty()) {
1080 // Paint the tiles on the tile map.
1081 if (!p_erase && random_tile_toggle->is_pressed()) {
1082 // Paint a random tile.
1083 Vector<Vector2i> line = TileMapEditor::get_line(tile_map, tile_map->local_to_map(p_from_mouse_pos), tile_map->local_to_map(p_to_mouse_pos));
1084 for (int i = 0; i < line.size(); i++) {
1085 output.insert(line[i], _pick_random_tile(pattern));
1086 }
1087 } else {
1088 // Paint the pattern.
1089 // If we paint several tiles, we virtually move the mouse as if it was in the center of the "brush"
1090 Vector2 mouse_offset = (Vector2(pattern->get_size()) / 2.0 - Vector2(0.5, 0.5)) * tile_set->get_tile_size();
1091 Vector2i last_hovered_cell = tile_map->local_to_map(p_from_mouse_pos - mouse_offset);
1092 Vector2i new_hovered_cell = tile_map->local_to_map(p_to_mouse_pos - mouse_offset);
1093 Vector2i drag_start_cell = tile_map->local_to_map(p_start_drag_mouse_pos - mouse_offset);
1094
1095 TypedArray<Vector2i> used_cells = pattern->get_used_cells();
1096 Vector2i offset = Vector2i(Math::posmod(drag_start_cell.x, pattern->get_size().x), Math::posmod(drag_start_cell.y, pattern->get_size().y)); // Note: no posmodv for Vector2i for now. Meh.s
1097 Vector<Vector2i> line = TileMapEditor::get_line(tile_map, (last_hovered_cell - offset) / pattern->get_size(), (new_hovered_cell - offset) / pattern->get_size());
1098 for (int i = 0; i < line.size(); i++) {
1099 Vector2i top_left = line[i] * pattern->get_size() + offset;
1100 for (int j = 0; j < used_cells.size(); j++) {
1101 Vector2i coords = tile_map->map_pattern(top_left, used_cells[j], pattern);
1102 output.insert(coords, TileMapCell(pattern->get_cell_source_id(used_cells[j]), pattern->get_cell_atlas_coords(used_cells[j]), pattern->get_cell_alternative_tile(used_cells[j])));
1103 }
1104 }
1105 }
1106 }
1107 return output;
1108}
1109
1110HashMap<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_rect(Vector2i p_start_cell, Vector2i p_end_cell, bool p_erase) {
1111 TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
1112 if (!tile_map) {
1113 return HashMap<Vector2i, TileMapCell>();
1114 }
1115
1116 Ref<TileSet> tile_set = tile_map->get_tileset();
1117 if (!tile_set.is_valid()) {
1118 return HashMap<Vector2i, TileMapCell>();
1119 }
1120
1121 // Create the rect to draw.
1122 Rect2i rect = Rect2i(p_start_cell, p_end_cell - p_start_cell).abs();
1123 rect.size += Vector2i(1, 1);
1124
1125 // Get or create the pattern.
1126 Ref<TileMapPattern> erase_pattern;
1127 erase_pattern.instantiate();
1128 erase_pattern->set_cell(Vector2i(0, 0), TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE);
1129 Ref<TileMapPattern> pattern = p_erase ? erase_pattern : selection_pattern;
1130
1131 HashMap<Vector2i, TileMapCell> err_output;
1132 ERR_FAIL_COND_V(pattern->is_empty(), err_output);
1133
1134 // Compute the offset to align things to the bottom or right.
1135 bool aligned_right = p_end_cell.x < p_start_cell.x;
1136 bool valigned_bottom = p_end_cell.y < p_start_cell.y;
1137 Vector2i offset = Vector2i(aligned_right ? -(pattern->get_size().x - (rect.get_size().x % pattern->get_size().x)) : 0, valigned_bottom ? -(pattern->get_size().y - (rect.get_size().y % pattern->get_size().y)) : 0);
1138
1139 HashMap<Vector2i, TileMapCell> output;
1140 if (!pattern->is_empty()) {
1141 if (!p_erase && random_tile_toggle->is_pressed()) {
1142 // Paint a random tile.
1143 for (int x = 0; x < rect.size.x; x++) {
1144 for (int y = 0; y < rect.size.y; y++) {
1145 Vector2i coords = rect.position + Vector2i(x, y);
1146 output.insert(coords, _pick_random_tile(pattern));
1147 }
1148 }
1149 } else {
1150 // Paint the pattern.
1151 TypedArray<Vector2i> used_cells = pattern->get_used_cells();
1152 for (int x = 0; x <= rect.size.x / pattern->get_size().x; x++) {
1153 for (int y = 0; y <= rect.size.y / pattern->get_size().y; y++) {
1154 Vector2i pattern_coords = rect.position + Vector2i(x, y) * pattern->get_size() + offset;
1155 for (int j = 0; j < used_cells.size(); j++) {
1156 Vector2i coords = pattern_coords + used_cells[j];
1157 if (rect.has_point(coords)) {
1158 output.insert(coords, TileMapCell(pattern->get_cell_source_id(used_cells[j]), pattern->get_cell_atlas_coords(used_cells[j]), pattern->get_cell_alternative_tile(used_cells[j])));
1159 }
1160 }
1161 }
1162 }
1163 }
1164 }
1165
1166 return output;
1167}
1168
1169HashMap<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_bucket_fill(Vector2i p_coords, bool p_contiguous, bool p_erase) {
1170 TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
1171 if (!tile_map) {
1172 return HashMap<Vector2i, TileMapCell>();
1173 }
1174
1175 if (tile_map_layer < 0) {
1176 return HashMap<Vector2i, TileMapCell>();
1177 }
1178 HashMap<Vector2i, TileMapCell> output;
1179 ERR_FAIL_INDEX_V(tile_map_layer, tile_map->get_layers_count(), output);
1180
1181 Ref<TileSet> tile_set = tile_map->get_tileset();
1182 if (!tile_set.is_valid()) {
1183 return HashMap<Vector2i, TileMapCell>();
1184 }
1185
1186 // Get or create the pattern.
1187 Ref<TileMapPattern> erase_pattern;
1188 erase_pattern.instantiate();
1189 erase_pattern->set_cell(Vector2i(0, 0), TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE);
1190 Ref<TileMapPattern> pattern = p_erase ? erase_pattern : selection_pattern;
1191
1192 if (!pattern->is_empty()) {
1193 TileMapCell source_cell = tile_map->get_cell(tile_map_layer, p_coords);
1194
1195 // If we are filling empty tiles, compute the tilemap boundaries.
1196 Rect2i boundaries;
1197 if (source_cell.source_id == TileSet::INVALID_SOURCE) {
1198 boundaries = tile_map->get_used_rect();
1199 }
1200
1201 if (p_contiguous) {
1202 // Replace continuous tiles like the source.
1203 RBSet<Vector2i> already_checked;
1204 List<Vector2i> to_check;
1205 to_check.push_back(p_coords);
1206 while (!to_check.is_empty()) {
1207 Vector2i coords = to_check.back()->get();
1208 to_check.pop_back();
1209 if (!already_checked.has(coords)) {
1210 if (source_cell.source_id == tile_map->get_cell_source_id(tile_map_layer, coords) &&
1211 source_cell.get_atlas_coords() == tile_map->get_cell_atlas_coords(tile_map_layer, coords) &&
1212 source_cell.alternative_tile == tile_map->get_cell_alternative_tile(tile_map_layer, coords) &&
1213 (source_cell.source_id != TileSet::INVALID_SOURCE || boundaries.has_point(coords))) {
1214 if (!p_erase && random_tile_toggle->is_pressed()) {
1215 // Paint a random tile.
1216 output.insert(coords, _pick_random_tile(pattern));
1217 } else {
1218 // Paint the pattern.
1219 Vector2i pattern_coords = (coords - p_coords) % pattern->get_size(); // Note: it would be good to have posmodv for Vector2i.
1220 pattern_coords.x = pattern_coords.x < 0 ? pattern_coords.x + pattern->get_size().x : pattern_coords.x;
1221 pattern_coords.y = pattern_coords.y < 0 ? pattern_coords.y + pattern->get_size().y : pattern_coords.y;
1222 if (pattern->has_cell(pattern_coords)) {
1223 output.insert(coords, TileMapCell(pattern->get_cell_source_id(pattern_coords), pattern->get_cell_atlas_coords(pattern_coords), pattern->get_cell_alternative_tile(pattern_coords)));
1224 } else {
1225 output.insert(coords, TileMapCell());
1226 }
1227 }
1228
1229 // Get surrounding tiles (handles different tile shapes).
1230 TypedArray<Vector2i> around = tile_map->get_surrounding_cells(coords);
1231 for (int i = 0; i < around.size(); i++) {
1232 to_check.push_back(around[i]);
1233 }
1234 }
1235 already_checked.insert(coords);
1236 }
1237 }
1238 } else {
1239 // Replace all tiles like the source.
1240 TypedArray<Vector2i> to_check;
1241 if (source_cell.source_id == TileSet::INVALID_SOURCE) {
1242 Rect2i rect = tile_map->get_used_rect();
1243 if (!rect.has_area()) {
1244 rect = Rect2i(p_coords, Vector2i(1, 1));
1245 }
1246 for (int x = boundaries.position.x; x < boundaries.get_end().x; x++) {
1247 for (int y = boundaries.position.y; y < boundaries.get_end().y; y++) {
1248 to_check.append(Vector2i(x, y));
1249 }
1250 }
1251 } else {
1252 to_check = tile_map->get_used_cells(tile_map_layer);
1253 }
1254 for (int i = 0; i < to_check.size(); i++) {
1255 Vector2i coords = to_check[i];
1256 if (source_cell.source_id == tile_map->get_cell_source_id(tile_map_layer, coords) &&
1257 source_cell.get_atlas_coords() == tile_map->get_cell_atlas_coords(tile_map_layer, coords) &&
1258 source_cell.alternative_tile == tile_map->get_cell_alternative_tile(tile_map_layer, coords) &&
1259 (source_cell.source_id != TileSet::INVALID_SOURCE || boundaries.has_point(coords))) {
1260 if (!p_erase && random_tile_toggle->is_pressed()) {
1261 // Paint a random tile.
1262 output.insert(coords, _pick_random_tile(pattern));
1263 } else {
1264 // Paint the pattern.
1265 Vector2i pattern_coords = (coords - p_coords) % pattern->get_size(); // Note: it would be good to have posmodv for Vector2i.
1266 pattern_coords.x = pattern_coords.x < 0 ? pattern_coords.x + pattern->get_size().x : pattern_coords.x;
1267 pattern_coords.y = pattern_coords.y < 0 ? pattern_coords.y + pattern->get_size().y : pattern_coords.y;
1268 if (pattern->has_cell(pattern_coords)) {
1269 output.insert(coords, TileMapCell(pattern->get_cell_source_id(pattern_coords), pattern->get_cell_atlas_coords(pattern_coords), pattern->get_cell_alternative_tile(pattern_coords)));
1270 } else {
1271 output.insert(coords, TileMapCell());
1272 }
1273 }
1274 }
1275 }
1276 }
1277 }
1278 return output;
1279}
1280
1281void TileMapEditorTilesPlugin::_stop_dragging() {
1282 if (drag_type == DRAG_TYPE_NONE) {
1283 return;
1284 }
1285
1286 TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
1287 if (!tile_map) {
1288 return;
1289 }
1290
1291 if (tile_map_layer < 0) {
1292 return;
1293 }
1294 ERR_FAIL_INDEX(tile_map_layer, tile_map->get_layers_count());
1295
1296 Ref<TileSet> tile_set = tile_map->get_tileset();
1297 if (!tile_set.is_valid()) {
1298 return;
1299 }
1300
1301 Transform2D xform = CanvasItemEditor::get_singleton()->get_canvas_transform() * tile_map->get_global_transform_with_canvas();
1302 Vector2 mpos = xform.affine_inverse().xform(CanvasItemEditor::get_singleton()->get_viewport_control()->get_local_mouse_position());
1303
1304 EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
1305 switch (drag_type) {
1306 case DRAG_TYPE_SELECT: {
1307 undo_redo->create_action(TTR("Change selection"));
1308 undo_redo->add_undo_method(this, "_set_tile_map_selection", _get_tile_map_selection());
1309
1310 if (!Input::get_singleton()->is_key_pressed(Key::SHIFT) && !Input::get_singleton()->is_key_pressed(Key::CMD_OR_CTRL)) {
1311 tile_map_selection.clear();
1312 }
1313 Rect2i rect = Rect2i(tile_map->local_to_map(drag_start_mouse_pos), tile_map->local_to_map(mpos) - tile_map->local_to_map(drag_start_mouse_pos)).abs();
1314 for (int x = rect.position.x; x <= rect.get_end().x; x++) {
1315 for (int y = rect.position.y; y <= rect.get_end().y; y++) {
1316 Vector2i coords = Vector2i(x, y);
1317 if (Input::get_singleton()->is_key_pressed(Key::CMD_OR_CTRL)) {
1318 if (tile_map_selection.has(coords)) {
1319 tile_map_selection.erase(coords);
1320 }
1321 } else {
1322 if (tile_map->get_cell_source_id(tile_map_layer, coords) != TileSet::INVALID_SOURCE) {
1323 tile_map_selection.insert(coords);
1324 }
1325 }
1326 }
1327 }
1328 undo_redo->add_do_method(this, "_set_tile_map_selection", _get_tile_map_selection());
1329 undo_redo->commit_action(false);
1330
1331 _update_selection_pattern_from_tilemap_selection();
1332 _update_tileset_selection_from_selection_pattern();
1333 } break;
1334 case DRAG_TYPE_MOVE: {
1335 if (patterns_item_list->is_visible_in_tree() && patterns_item_list->has_point(patterns_item_list->get_local_mouse_position())) {
1336 // Restore the cells.
1337 for (KeyValue<Vector2i, TileMapCell> kv : drag_modified) {
1338 tile_map->set_cell(tile_map_layer, kv.key, kv.value.source_id, kv.value.get_atlas_coords(), kv.value.alternative_tile);
1339 }
1340
1341 if (!EditorNode::get_singleton()->is_resource_read_only(tile_set)) {
1342 // Creating a pattern in the pattern list.
1343 select_last_pattern = true;
1344 int new_pattern_index = tile_set->get_patterns_count();
1345 undo_redo->create_action(TTR("Add TileSet pattern"));
1346 undo_redo->add_do_method(*tile_set, "add_pattern", selection_pattern, new_pattern_index);
1347 undo_redo->add_undo_method(*tile_set, "remove_pattern", new_pattern_index);
1348 undo_redo->commit_action();
1349 }
1350 } else {
1351 // Get the top-left cell.
1352 Vector2i top_left;
1353 if (!tile_map_selection.is_empty()) {
1354 top_left = tile_map_selection.front()->get();
1355 }
1356 for (const Vector2i &E : tile_map_selection) {
1357 top_left = top_left.min(E);
1358 }
1359
1360 // Get the offset from the mouse.
1361 Vector2i offset = drag_start_mouse_pos - tile_map->map_to_local(top_left);
1362 offset = tile_map->local_to_map(mpos - offset) - tile_map->local_to_map(drag_start_mouse_pos - offset);
1363
1364 TypedArray<Vector2i> selection_used_cells = selection_pattern->get_used_cells();
1365
1366 // Build the list of cells to undo.
1367 Vector2i coords;
1368 HashMap<Vector2i, TileMapCell> cells_undo;
1369 for (int i = 0; i < selection_used_cells.size(); i++) {
1370 coords = tile_map->map_pattern(top_left, selection_used_cells[i], selection_pattern);
1371 cells_undo[coords] = TileMapCell(drag_modified[coords].source_id, drag_modified[coords].get_atlas_coords(), drag_modified[coords].alternative_tile);
1372 coords = tile_map->map_pattern(top_left + offset, selection_used_cells[i], selection_pattern);
1373 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));
1374 }
1375
1376 // Build the list of cells to do.
1377 HashMap<Vector2i, TileMapCell> cells_do;
1378 for (int i = 0; i < selection_used_cells.size(); i++) {
1379 coords = tile_map->map_pattern(top_left, selection_used_cells[i], selection_pattern);
1380 cells_do[coords] = TileMapCell();
1381 }
1382 for (int i = 0; i < selection_used_cells.size(); i++) {
1383 coords = tile_map->map_pattern(top_left + offset, selection_used_cells[i], selection_pattern);
1384 cells_do[coords] = TileMapCell(selection_pattern->get_cell_source_id(selection_used_cells[i]), selection_pattern->get_cell_atlas_coords(selection_used_cells[i]), selection_pattern->get_cell_alternative_tile(selection_used_cells[i]));
1385 }
1386
1387 // Move the tiles.
1388 undo_redo->create_action(TTR("Move tiles"));
1389 for (const KeyValue<Vector2i, TileMapCell> &E : cells_do) {
1390 undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, E.key, E.value.source_id, E.value.get_atlas_coords(), E.value.alternative_tile);
1391 }
1392 for (const KeyValue<Vector2i, TileMapCell> &E : cells_undo) {
1393 undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, E.key, E.value.source_id, E.value.get_atlas_coords(), E.value.alternative_tile);
1394 }
1395
1396 // Update the selection.
1397 undo_redo->add_undo_method(this, "_set_tile_map_selection", _get_tile_map_selection());
1398 tile_map_selection.clear();
1399 for (int i = 0; i < selection_used_cells.size(); i++) {
1400 coords = tile_map->map_pattern(top_left + offset, selection_used_cells[i], selection_pattern);
1401 tile_map_selection.insert(coords);
1402 }
1403 undo_redo->add_do_method(this, "_set_tile_map_selection", _get_tile_map_selection());
1404 undo_redo->commit_action();
1405 }
1406 } break;
1407 case DRAG_TYPE_PICK: {
1408 Rect2i rect = Rect2i(tile_map->local_to_map(drag_start_mouse_pos), tile_map->local_to_map(mpos) - tile_map->local_to_map(drag_start_mouse_pos)).abs();
1409 rect.size += Vector2i(1, 1);
1410
1411 int picked_source = -1;
1412 TypedArray<Vector2i> coords_array;
1413 for (int x = rect.position.x; x < rect.get_end().x; x++) {
1414 for (int y = rect.position.y; y < rect.get_end().y; y++) {
1415 Vector2i coords = Vector2i(x, y);
1416
1417 int source = tile_map->get_cell_source_id(tile_map_layer, coords);
1418 if (source != TileSet::INVALID_SOURCE) {
1419 coords_array.push_back(coords);
1420 if (picked_source == -1) {
1421 picked_source = source;
1422 } else if (picked_source != source) {
1423 picked_source = -2;
1424 }
1425 }
1426 }
1427 }
1428
1429 if (picked_source >= 0) {
1430 for (int i = 0; i < sources_list->get_item_count(); i++) {
1431 if (int(sources_list->get_item_metadata(i)) == picked_source) {
1432 sources_list->set_current(i);
1433 TilesEditorUtils::get_singleton()->set_sources_lists_current(i);
1434 break;
1435 }
1436 }
1437 sources_list->ensure_current_is_visible();
1438 }
1439
1440 Ref<TileMapPattern> new_selection_pattern = tile_map->get_pattern(tile_map_layer, coords_array);
1441 if (!new_selection_pattern->is_empty()) {
1442 selection_pattern = new_selection_pattern;
1443 _update_tileset_selection_from_selection_pattern();
1444 }
1445 picker_button->set_pressed(false);
1446 } break;
1447 case DRAG_TYPE_PAINT: {
1448 undo_redo->create_action(TTR("Paint tiles"));
1449 for (const KeyValue<Vector2i, TileMapCell> &E : drag_modified) {
1450 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));
1451 undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, E.key, E.value.source_id, E.value.get_atlas_coords(), E.value.alternative_tile);
1452 }
1453 undo_redo->commit_action(false);
1454 } break;
1455 case DRAG_TYPE_LINE: {
1456 HashMap<Vector2i, TileMapCell> to_draw = _draw_line(drag_start_mouse_pos, drag_start_mouse_pos, mpos, drag_erasing);
1457 undo_redo->create_action(TTR("Paint tiles"));
1458 for (const KeyValue<Vector2i, TileMapCell> &E : to_draw) {
1459 if (!drag_erasing && E.value.source_id == TileSet::INVALID_SOURCE) {
1460 continue;
1461 }
1462 undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, E.key, E.value.source_id, E.value.get_atlas_coords(), E.value.alternative_tile);
1463 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));
1464 }
1465 undo_redo->commit_action();
1466 } break;
1467 case DRAG_TYPE_RECT: {
1468 HashMap<Vector2i, TileMapCell> to_draw = _draw_rect(tile_map->local_to_map(drag_start_mouse_pos), tile_map->local_to_map(mpos), drag_erasing);
1469 undo_redo->create_action(TTR("Paint tiles"));
1470 for (const KeyValue<Vector2i, TileMapCell> &E : to_draw) {
1471 if (!drag_erasing && E.value.source_id == TileSet::INVALID_SOURCE) {
1472 continue;
1473 }
1474 undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, E.key, E.value.source_id, E.value.get_atlas_coords(), E.value.alternative_tile);
1475 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));
1476 }
1477 undo_redo->commit_action();
1478 } break;
1479 case DRAG_TYPE_BUCKET: {
1480 undo_redo->create_action(TTR("Paint tiles"));
1481 for (const KeyValue<Vector2i, TileMapCell> &E : drag_modified) {
1482 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));
1483 undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, E.key, E.value.source_id, E.value.get_atlas_coords(), E.value.alternative_tile);
1484 }
1485 undo_redo->commit_action(false);
1486 } break;
1487 case DRAG_TYPE_CLIPBOARD_PASTE: {
1488 Vector2 mouse_offset = (Vector2(tile_map_clipboard->get_size()) / 2.0 - Vector2(0.5, 0.5)) * tile_set->get_tile_size();
1489 undo_redo->create_action(TTR("Paste tiles"));
1490 TypedArray<Vector2i> used_cells = tile_map_clipboard->get_used_cells();
1491 for (int i = 0; i < used_cells.size(); i++) {
1492 Vector2i coords = tile_map->map_pattern(tile_map->local_to_map(mpos - mouse_offset), used_cells[i], tile_map_clipboard);
1493 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]));
1494 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));
1495 }
1496 undo_redo->commit_action();
1497 } break;
1498 default:
1499 break;
1500 }
1501 drag_type = DRAG_TYPE_NONE;
1502}
1503
1504void TileMapEditorTilesPlugin::_apply_transform(int p_type) {
1505 if (selection_pattern.is_null() || selection_pattern->is_empty()) {
1506 return;
1507 }
1508
1509 Ref<TileMapPattern> transformed_pattern;
1510 transformed_pattern.instantiate();
1511 bool keep_shape = selection_pattern->get_size() == Vector2i(1, 1);
1512
1513 Vector2i size = selection_pattern->get_size();
1514 for (int y = 0; y < size.y; y++) {
1515 for (int x = 0; x < size.x; x++) {
1516 Vector2i src_coords = Vector2i(x, y);
1517 if (!selection_pattern->has_cell(src_coords)) {
1518 continue;
1519 }
1520
1521 Vector2i dst_coords;
1522
1523 if (keep_shape) {
1524 dst_coords = src_coords;
1525 } else if (p_type == TRANSFORM_ROTATE_LEFT) {
1526 dst_coords = Vector2i(y, size.x - x - 1);
1527 } else if (p_type == TRANSFORM_ROTATE_RIGHT) {
1528 dst_coords = Vector2i(size.y - y - 1, x);
1529 } else if (p_type == TRANSFORM_FLIP_H) {
1530 dst_coords = Vector2i(size.x - x - 1, y);
1531 } else if (p_type == TRANSFORM_FLIP_V) {
1532 dst_coords = Vector2i(x, size.y - y - 1);
1533 }
1534
1535 transformed_pattern->set_cell(dst_coords,
1536 selection_pattern->get_cell_source_id(src_coords), selection_pattern->get_cell_atlas_coords(src_coords),
1537 _get_transformed_alternative(selection_pattern->get_cell_alternative_tile(src_coords), p_type));
1538 }
1539 }
1540 selection_pattern = transformed_pattern;
1541 CanvasItemEditor::get_singleton()->update_viewport();
1542}
1543
1544int TileMapEditorTilesPlugin::_get_transformed_alternative(int p_alternative_id, int p_transform) {
1545 bool transform_flip_h = p_alternative_id & TileSetAtlasSource::TRANSFORM_FLIP_H;
1546 bool transform_flip_v = p_alternative_id & TileSetAtlasSource::TRANSFORM_FLIP_V;
1547 bool transform_transpose = p_alternative_id & TileSetAtlasSource::TRANSFORM_TRANSPOSE;
1548
1549 switch (p_transform) {
1550 case TRANSFORM_ROTATE_LEFT:
1551 case TRANSFORM_ROTATE_RIGHT: {
1552 // A matrix with every possible flip/transpose combination, sorted by what comes next when you rotate.
1553 const LocalVector<bool> rotation_matrix = {
1554 0, 0, 0,
1555 0, 1, 1,
1556 1, 1, 0,
1557 1, 0, 1,
1558 1, 0, 0,
1559 0, 0, 1,
1560 0, 1, 0,
1561 1, 1, 1
1562 };
1563
1564 for (int i = 0; i < 8; i++) {
1565 if (transform_flip_h == rotation_matrix[i * 3] && transform_flip_v == rotation_matrix[i * 3 + 1] && transform_transpose == rotation_matrix[i * 3 + 2]) {
1566 if (p_transform == TRANSFORM_ROTATE_LEFT) {
1567 i = i / 4 * 4 + (i + 1) % 4;
1568 } else {
1569 i = i / 4 * 4 + Math::posmod(i - 1, 4);
1570 }
1571 transform_flip_h = rotation_matrix[i * 3];
1572 transform_flip_v = rotation_matrix[i * 3 + 1];
1573 transform_transpose = rotation_matrix[i * 3 + 2];
1574 break;
1575 }
1576 }
1577 } break;
1578 case TRANSFORM_FLIP_H: {
1579 transform_flip_h = !transform_flip_h;
1580 } break;
1581 case TRANSFORM_FLIP_V: {
1582 transform_flip_v = !transform_flip_v;
1583 } break;
1584 }
1585
1586 return TileSetAtlasSource::alternative_no_transform(p_alternative_id) |
1587 int(transform_flip_h) * TileSetAtlasSource::TRANSFORM_FLIP_H |
1588 int(transform_flip_v) * TileSetAtlasSource::TRANSFORM_FLIP_V |
1589 int(transform_transpose) * TileSetAtlasSource::TRANSFORM_TRANSPOSE;
1590}
1591
1592void TileMapEditorTilesPlugin::_update_fix_selected_and_hovered() {
1593 TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
1594 if (!tile_map) {
1595 hovered_tile.source_id = TileSet::INVALID_SOURCE;
1596 hovered_tile.set_atlas_coords(TileSetSource::INVALID_ATLAS_COORDS);
1597 hovered_tile.alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE;
1598 tile_set_selection.clear();
1599 patterns_item_list->deselect_all();
1600 tile_map_selection.clear();
1601 selection_pattern.instantiate();
1602 return;
1603 }
1604
1605 Ref<TileSet> tile_set = tile_map->get_tileset();
1606 if (!tile_set.is_valid()) {
1607 hovered_tile.source_id = TileSet::INVALID_SOURCE;
1608 hovered_tile.set_atlas_coords(TileSetSource::INVALID_ATLAS_COORDS);
1609 hovered_tile.alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE;
1610 tile_set_selection.clear();
1611 patterns_item_list->deselect_all();
1612 tile_map_selection.clear();
1613 selection_pattern.instantiate();
1614 return;
1615 }
1616
1617 int source_index = sources_list->get_current();
1618 if (source_index < 0 || source_index >= sources_list->get_item_count()) {
1619 hovered_tile.source_id = TileSet::INVALID_SOURCE;
1620 hovered_tile.set_atlas_coords(TileSetSource::INVALID_ATLAS_COORDS);
1621 hovered_tile.alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE;
1622 tile_set_selection.clear();
1623 patterns_item_list->deselect_all();
1624 tile_map_selection.clear();
1625 selection_pattern.instantiate();
1626 return;
1627 }
1628
1629 int source_id = sources_list->get_item_metadata(source_index);
1630
1631 // Clear hovered if needed.
1632 if (source_id != hovered_tile.source_id ||
1633 !tile_set->has_source(hovered_tile.source_id) ||
1634 !tile_set->get_source(hovered_tile.source_id)->has_tile(hovered_tile.get_atlas_coords()) ||
1635 !tile_set->get_source(hovered_tile.source_id)->has_alternative_tile(hovered_tile.get_atlas_coords(), hovered_tile.alternative_tile)) {
1636 hovered_tile.source_id = TileSet::INVALID_SOURCE;
1637 hovered_tile.set_atlas_coords(TileSetSource::INVALID_ATLAS_COORDS);
1638 hovered_tile.alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE;
1639 }
1640
1641 // Selection if needed.
1642 for (RBSet<TileMapCell>::Element *E = tile_set_selection.front(); E;) {
1643 RBSet<TileMapCell>::Element *N = E->next();
1644 const TileMapCell *selected = &(E->get());
1645 if (!tile_set->has_source(selected->source_id) ||
1646 !tile_set->get_source(selected->source_id)->has_tile(selected->get_atlas_coords()) ||
1647 !tile_set->get_source(selected->source_id)->has_alternative_tile(selected->get_atlas_coords(), selected->alternative_tile)) {
1648 tile_set_selection.erase(E);
1649 }
1650 E = N;
1651 }
1652
1653 if (!tile_map_selection.is_empty()) {
1654 _update_selection_pattern_from_tilemap_selection();
1655 } else if (tiles_bottom_panel->is_visible_in_tree()) {
1656 _update_selection_pattern_from_tileset_tiles_selection();
1657 } else {
1658 _update_selection_pattern_from_tileset_pattern_selection();
1659 }
1660}
1661
1662void TileMapEditorTilesPlugin::_fix_invalid_tiles_in_tile_map_selection() {
1663 TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
1664 if (!tile_map) {
1665 return;
1666 }
1667
1668 RBSet<Vector2i> to_remove;
1669 for (Vector2i selected : tile_map_selection) {
1670 TileMapCell cell = tile_map->get_cell(tile_map_layer, selected);
1671 if (cell.source_id == TileSet::INVALID_SOURCE && cell.get_atlas_coords() == TileSetSource::INVALID_ATLAS_COORDS && cell.alternative_tile == TileSetAtlasSource::INVALID_TILE_ALTERNATIVE) {
1672 to_remove.insert(selected);
1673 }
1674 }
1675
1676 for (Vector2i cell : to_remove) {
1677 tile_map_selection.erase(cell);
1678 }
1679}
1680void TileMapEditorTilesPlugin::patterns_item_list_empty_clicked(const Vector2 &p_pos, MouseButton p_mouse_button_index) {
1681 if (p_mouse_button_index == MouseButton::LEFT) {
1682 _update_selection_pattern_from_tileset_pattern_selection();
1683 }
1684}
1685
1686void TileMapEditorTilesPlugin::_update_selection_pattern_from_tilemap_selection() {
1687 TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
1688 if (!tile_map) {
1689 return;
1690 }
1691
1692 Ref<TileSet> tile_set = tile_map->get_tileset();
1693 if (!tile_set.is_valid()) {
1694 return;
1695 }
1696
1697 ERR_FAIL_INDEX(tile_map_layer, tile_map->get_layers_count());
1698
1699 selection_pattern.instantiate();
1700
1701 TypedArray<Vector2i> coords_array;
1702 for (const Vector2i &E : tile_map_selection) {
1703 coords_array.push_back(E);
1704 }
1705 selection_pattern = tile_map->get_pattern(tile_map_layer, coords_array);
1706 _update_transform_buttons();
1707}
1708
1709void TileMapEditorTilesPlugin::_update_selection_pattern_from_tileset_tiles_selection() {
1710 TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
1711 if (!tile_map) {
1712 return;
1713 }
1714
1715 Ref<TileSet> tile_set = tile_map->get_tileset();
1716 if (!tile_set.is_valid()) {
1717 return;
1718 }
1719
1720 // Clear the tilemap selection.
1721 tile_map_selection.clear();
1722
1723 // Clear the selected pattern.
1724 selection_pattern.instantiate();
1725
1726 // Group per source.
1727 HashMap<int, List<const TileMapCell *>> per_source;
1728 for (const TileMapCell &E : tile_set_selection) {
1729 per_source[E.source_id].push_back(&(E));
1730 }
1731
1732 int vertical_offset = 0;
1733 for (const KeyValue<int, List<const TileMapCell *>> &E_source : per_source) {
1734 // Per source.
1735 List<const TileMapCell *> unorganized;
1736 Rect2i encompassing_rect_coords;
1737 HashMap<Vector2i, const TileMapCell *> organized_pattern;
1738
1739 TileSetSource *source = *tile_set->get_source(E_source.key);
1740 TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
1741 if (atlas_source) {
1742 // Organize using coordinates.
1743 for (const TileMapCell *current : E_source.value) {
1744 if (current->alternative_tile == 0) {
1745 organized_pattern[current->get_atlas_coords()] = current;
1746 } else {
1747 unorganized.push_back(current);
1748 }
1749 }
1750
1751 // Compute the encompassing rect for the organized pattern.
1752 HashMap<Vector2i, const TileMapCell *>::Iterator E_cell = organized_pattern.begin();
1753 if (E_cell) {
1754 encompassing_rect_coords = Rect2i(E_cell->key, Vector2i(1, 1));
1755 for (; E_cell; ++E_cell) {
1756 encompassing_rect_coords.expand_to(E_cell->key + Vector2i(1, 1));
1757 encompassing_rect_coords.expand_to(E_cell->key);
1758 }
1759 }
1760 } else {
1761 // Add everything unorganized.
1762 for (const TileMapCell *cell : E_source.value) {
1763 unorganized.push_back(cell);
1764 }
1765 }
1766
1767 // Now add everything to the output pattern.
1768 for (const KeyValue<Vector2i, const TileMapCell *> &E_cell : organized_pattern) {
1769 selection_pattern->set_cell(E_cell.key - encompassing_rect_coords.position + Vector2i(0, vertical_offset), E_cell.value->source_id, E_cell.value->get_atlas_coords(), E_cell.value->alternative_tile);
1770 }
1771 Vector2i organized_size = selection_pattern->get_size();
1772 int unorganized_index = 0;
1773 for (const TileMapCell *cell : unorganized) {
1774 selection_pattern->set_cell(Vector2(organized_size.x + unorganized_index, vertical_offset), cell->source_id, cell->get_atlas_coords(), cell->alternative_tile);
1775 unorganized_index++;
1776 }
1777 vertical_offset += MAX(organized_size.y, 1);
1778 }
1779 CanvasItemEditor::get_singleton()->update_viewport();
1780 _update_transform_buttons();
1781}
1782
1783void TileMapEditorTilesPlugin::_update_selection_pattern_from_tileset_pattern_selection() {
1784 TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
1785 if (!tile_map) {
1786 return;
1787 }
1788
1789 Ref<TileSet> tile_set = tile_map->get_tileset();
1790 if (!tile_set.is_valid()) {
1791 return;
1792 }
1793
1794 // Clear the tilemap selection.
1795 tile_map_selection.clear();
1796
1797 // Clear the selected pattern.
1798 selection_pattern.instantiate();
1799
1800 if (patterns_item_list->get_selected_items().size() >= 1) {
1801 selection_pattern = patterns_item_list->get_item_metadata(patterns_item_list->get_selected_items()[0]);
1802 }
1803
1804 CanvasItemEditor::get_singleton()->update_viewport();
1805}
1806
1807void TileMapEditorTilesPlugin::_update_tileset_selection_from_selection_pattern() {
1808 tile_set_selection.clear();
1809 TypedArray<Vector2i> used_cells = selection_pattern->get_used_cells();
1810 for (int i = 0; i < used_cells.size(); i++) {
1811 Vector2i coords = used_cells[i];
1812 if (selection_pattern->get_cell_source_id(coords) != TileSet::INVALID_SOURCE) {
1813 tile_set_selection.insert(TileMapCell(selection_pattern->get_cell_source_id(coords), selection_pattern->get_cell_atlas_coords(coords), selection_pattern->get_cell_alternative_tile(coords)));
1814 }
1815 }
1816 _update_source_display();
1817 tile_atlas_control->queue_redraw();
1818 alternative_tiles_control->queue_redraw();
1819 _update_transform_buttons();
1820}
1821
1822void TileMapEditorTilesPlugin::_tile_atlas_control_draw() {
1823 TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
1824 if (!tile_map) {
1825 return;
1826 }
1827
1828 Ref<TileSet> tile_set = tile_map->get_tileset();
1829 if (!tile_set.is_valid()) {
1830 return;
1831 }
1832
1833 int source_index = sources_list->get_current();
1834 if (source_index < 0 || source_index >= sources_list->get_item_count()) {
1835 return;
1836 }
1837
1838 int source_id = sources_list->get_item_metadata(source_index);
1839 if (!tile_set->has_source(source_id)) {
1840 return;
1841 }
1842
1843 TileSetAtlasSource *atlas = Object::cast_to<TileSetAtlasSource>(*tile_set->get_source(source_id));
1844 if (!atlas) {
1845 return;
1846 }
1847
1848 // Draw the selection.
1849 Color grid_color = EDITOR_GET("editors/tiles_editor/grid_color");
1850 Color selection_color = Color().from_hsv(Math::fposmod(grid_color.get_h() + 0.5, 1.0), grid_color.get_s(), grid_color.get_v(), 1.0);
1851 for (const TileMapCell &E : tile_set_selection) {
1852 if (E.source_id == source_id && E.alternative_tile == 0) {
1853 for (int frame = 0; frame < atlas->get_tile_animation_frames_count(E.get_atlas_coords()); frame++) {
1854 Color color = selection_color;
1855 if (frame > 0) {
1856 color.a *= 0.3;
1857 }
1858 TilesEditorUtils::draw_selection_rect(tile_atlas_control, atlas->get_tile_texture_region(E.get_atlas_coords(), frame), color);
1859 }
1860 }
1861 }
1862
1863 // Draw the hovered tile.
1864 if (hovered_tile.get_atlas_coords() != TileSetSource::INVALID_ATLAS_COORDS && hovered_tile.alternative_tile == 0 && !tile_set_dragging_selection) {
1865 for (int frame = 0; frame < atlas->get_tile_animation_frames_count(hovered_tile.get_atlas_coords()); frame++) {
1866 Color color = Color(1.0, 0.8, 0.0, frame == 0 ? 0.6 : 0.3);
1867 TilesEditorUtils::draw_selection_rect(tile_atlas_control, atlas->get_tile_texture_region(hovered_tile.get_atlas_coords(), frame), color);
1868 }
1869 }
1870
1871 // Draw the selection rect.
1872 if (tile_set_dragging_selection) {
1873 Vector2i start_tile = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_set_drag_start_mouse_pos, true);
1874 Vector2i end_tile = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position(), true);
1875
1876 Rect2i region = Rect2i(start_tile, end_tile - start_tile).abs();
1877 region.size += Vector2i(1, 1);
1878
1879 RBSet<Vector2i> to_draw;
1880 for (int x = region.position.x; x < region.get_end().x; x++) {
1881 for (int y = region.position.y; y < region.get_end().y; y++) {
1882 Vector2i tile = atlas->get_tile_at_coords(Vector2i(x, y));
1883 if (tile != TileSetSource::INVALID_ATLAS_COORDS) {
1884 to_draw.insert(tile);
1885 }
1886 }
1887 }
1888 for (const Vector2i &E : to_draw) {
1889 TilesEditorUtils::draw_selection_rect(tile_atlas_control, atlas->get_tile_texture_region(E));
1890 }
1891 }
1892}
1893
1894void TileMapEditorTilesPlugin::_tile_atlas_control_mouse_exited() {
1895 hovered_tile.source_id = TileSet::INVALID_SOURCE;
1896 hovered_tile.set_atlas_coords(TileSetSource::INVALID_ATLAS_COORDS);
1897 hovered_tile.alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE;
1898 tile_atlas_control->queue_redraw();
1899}
1900
1901void TileMapEditorTilesPlugin::_tile_atlas_control_gui_input(const Ref<InputEvent> &p_event) {
1902 TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
1903 if (!tile_map) {
1904 return;
1905 }
1906
1907 Ref<TileSet> tile_set = tile_map->get_tileset();
1908 if (!tile_set.is_valid()) {
1909 return;
1910 }
1911
1912 int source_index = sources_list->get_current();
1913 if (source_index < 0 || source_index >= sources_list->get_item_count()) {
1914 return;
1915 }
1916
1917 int source_id = sources_list->get_item_metadata(source_index);
1918 if (!tile_set->has_source(source_id)) {
1919 return;
1920 }
1921
1922 TileSetAtlasSource *atlas = Object::cast_to<TileSetAtlasSource>(*tile_set->get_source(source_id));
1923 if (!atlas) {
1924 return;
1925 }
1926
1927 // Update the hovered tile
1928 hovered_tile.source_id = source_id;
1929 hovered_tile.set_atlas_coords(TileSetSource::INVALID_ATLAS_COORDS);
1930 hovered_tile.alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE;
1931 Vector2i coords = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position());
1932 if (coords != TileSetSource::INVALID_ATLAS_COORDS) {
1933 coords = atlas->get_tile_at_coords(coords);
1934 if (coords != TileSetSource::INVALID_ATLAS_COORDS) {
1935 hovered_tile.set_atlas_coords(coords);
1936 hovered_tile.alternative_tile = 0;
1937 }
1938 }
1939
1940 Ref<InputEventMouseMotion> mm = p_event;
1941 if (mm.is_valid()) {
1942 tile_atlas_control->queue_redraw();
1943 alternative_tiles_control->queue_redraw();
1944 }
1945
1946 Ref<InputEventMouseButton> mb = p_event;
1947 if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT) {
1948 if (mb->is_pressed()) { // Pressed
1949 tile_set_dragging_selection = true;
1950 tile_set_drag_start_mouse_pos = tile_atlas_control->get_local_mouse_position();
1951 if (!mb->is_shift_pressed()) {
1952 tile_set_selection.clear();
1953 }
1954
1955 if (hovered_tile.get_atlas_coords() != TileSetSource::INVALID_ATLAS_COORDS && hovered_tile.alternative_tile == 0) {
1956 if (mb->is_shift_pressed() && tile_set_selection.has(TileMapCell(source_id, hovered_tile.get_atlas_coords(), 0))) {
1957 tile_set_selection.erase(TileMapCell(source_id, hovered_tile.get_atlas_coords(), 0));
1958 } else {
1959 tile_set_selection.insert(TileMapCell(source_id, hovered_tile.get_atlas_coords(), 0));
1960 }
1961 }
1962 _update_selection_pattern_from_tileset_tiles_selection();
1963 } else { // Released
1964 if (tile_set_dragging_selection) {
1965 if (!mb->is_shift_pressed()) {
1966 tile_set_selection.clear();
1967 }
1968 // Compute the covered area.
1969 Vector2i start_tile = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_set_drag_start_mouse_pos, true);
1970 Vector2i end_tile = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position(), true);
1971 if (start_tile != TileSetSource::INVALID_ATLAS_COORDS && end_tile != TileSetSource::INVALID_ATLAS_COORDS) {
1972 Rect2i region = Rect2i(start_tile, end_tile - start_tile).abs();
1973 region.size += Vector2i(1, 1);
1974
1975 // To update the selection, we copy the selected/not selected status of the tiles we drag from.
1976 Vector2i start_coords = atlas->get_tile_at_coords(start_tile);
1977 if (mb->is_shift_pressed() && start_coords != TileSetSource::INVALID_ATLAS_COORDS && !tile_set_selection.has(TileMapCell(source_id, start_coords, 0))) {
1978 // Remove from the selection.
1979 for (int x = region.position.x; x < region.get_end().x; x++) {
1980 for (int y = region.position.y; y < region.get_end().y; y++) {
1981 Vector2i tile_coords = atlas->get_tile_at_coords(Vector2i(x, y));
1982 if (tile_coords != TileSetSource::INVALID_ATLAS_COORDS && tile_set_selection.has(TileMapCell(source_id, tile_coords, 0))) {
1983 tile_set_selection.erase(TileMapCell(source_id, tile_coords, 0));
1984 }
1985 }
1986 }
1987 } else {
1988 // Insert in the selection.
1989 for (int x = region.position.x; x < region.get_end().x; x++) {
1990 for (int y = region.position.y; y < region.get_end().y; y++) {
1991 Vector2i tile_coords = atlas->get_tile_at_coords(Vector2i(x, y));
1992 if (tile_coords != TileSetSource::INVALID_ATLAS_COORDS) {
1993 tile_set_selection.insert(TileMapCell(source_id, tile_coords, 0));
1994 }
1995 }
1996 }
1997 }
1998 }
1999 _update_selection_pattern_from_tileset_tiles_selection();
2000 }
2001 tile_set_dragging_selection = false;
2002 }
2003 tile_atlas_control->queue_redraw();
2004 }
2005}
2006
2007void TileMapEditorTilesPlugin::_tile_alternatives_control_draw() {
2008 TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
2009 if (!tile_map) {
2010 return;
2011 }
2012
2013 Ref<TileSet> tile_set = tile_map->get_tileset();
2014 if (!tile_set.is_valid()) {
2015 return;
2016 }
2017
2018 int source_index = sources_list->get_current();
2019 if (source_index < 0 || source_index >= sources_list->get_item_count()) {
2020 return;
2021 }
2022
2023 int source_id = sources_list->get_item_metadata(source_index);
2024 if (!tile_set->has_source(source_id)) {
2025 return;
2026 }
2027
2028 TileSetAtlasSource *atlas = Object::cast_to<TileSetAtlasSource>(*tile_set->get_source(source_id));
2029 if (!atlas) {
2030 return;
2031 }
2032
2033 // Draw the selection.
2034 for (const TileMapCell &E : tile_set_selection) {
2035 if (E.source_id == source_id && E.get_atlas_coords() != TileSetSource::INVALID_ATLAS_COORDS && E.alternative_tile > 0) {
2036 Rect2i rect = tile_atlas_view->get_alternative_tile_rect(E.get_atlas_coords(), E.alternative_tile);
2037 if (rect != Rect2i()) {
2038 TilesEditorUtils::draw_selection_rect(alternative_tiles_control, rect);
2039 }
2040 }
2041 }
2042
2043 // Draw hovered tile.
2044 if (hovered_tile.get_atlas_coords() != TileSetSource::INVALID_ATLAS_COORDS && hovered_tile.alternative_tile > 0) {
2045 Rect2i rect = tile_atlas_view->get_alternative_tile_rect(hovered_tile.get_atlas_coords(), hovered_tile.alternative_tile);
2046 if (rect != Rect2i()) {
2047 TilesEditorUtils::draw_selection_rect(alternative_tiles_control, rect, Color(1.0, 0.8, 0.0, 0.5));
2048 }
2049 }
2050}
2051
2052void TileMapEditorTilesPlugin::_tile_alternatives_control_mouse_exited() {
2053 hovered_tile.source_id = TileSet::INVALID_SOURCE;
2054 hovered_tile.set_atlas_coords(TileSetSource::INVALID_ATLAS_COORDS);
2055 hovered_tile.alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE;
2056 alternative_tiles_control->queue_redraw();
2057}
2058
2059void TileMapEditorTilesPlugin::_tile_alternatives_control_gui_input(const Ref<InputEvent> &p_event) {
2060 TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
2061 if (!tile_map) {
2062 return;
2063 }
2064
2065 Ref<TileSet> tile_set = tile_map->get_tileset();
2066 if (!tile_set.is_valid()) {
2067 return;
2068 }
2069
2070 int source_index = sources_list->get_current();
2071 if (source_index < 0 || source_index >= sources_list->get_item_count()) {
2072 return;
2073 }
2074
2075 int source_id = sources_list->get_item_metadata(source_index);
2076 if (!tile_set->has_source(source_id)) {
2077 return;
2078 }
2079
2080 TileSetAtlasSource *atlas = Object::cast_to<TileSetAtlasSource>(*tile_set->get_source(source_id));
2081 if (!atlas) {
2082 return;
2083 }
2084
2085 // Update the hovered tile
2086 hovered_tile.source_id = source_id;
2087 hovered_tile.set_atlas_coords(TileSetSource::INVALID_ATLAS_COORDS);
2088 hovered_tile.alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE;
2089 Vector3i alternative_coords = tile_atlas_view->get_alternative_tile_at_pos(alternative_tiles_control->get_local_mouse_position());
2090 Vector2i coords = Vector2i(alternative_coords.x, alternative_coords.y);
2091 int alternative = alternative_coords.z;
2092 if (coords != TileSetSource::INVALID_ATLAS_COORDS && alternative != TileSetSource::INVALID_TILE_ALTERNATIVE) {
2093 hovered_tile.set_atlas_coords(coords);
2094 hovered_tile.alternative_tile = alternative;
2095 }
2096
2097 Ref<InputEventMouseMotion> mm = p_event;
2098 if (mm.is_valid()) {
2099 tile_atlas_control->queue_redraw();
2100 alternative_tiles_control->queue_redraw();
2101 }
2102
2103 Ref<InputEventMouseButton> mb = p_event;
2104 if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT) {
2105 if (mb->is_pressed()) { // Pressed
2106 // Left click pressed.
2107 if (!mb->is_shift_pressed()) {
2108 tile_set_selection.clear();
2109 }
2110
2111 if (coords != TileSetSource::INVALID_ATLAS_COORDS && alternative != TileSetAtlasSource::INVALID_TILE_ALTERNATIVE) {
2112 if (mb->is_shift_pressed() && tile_set_selection.has(TileMapCell(source_id, hovered_tile.get_atlas_coords(), hovered_tile.alternative_tile))) {
2113 tile_set_selection.erase(TileMapCell(source_id, hovered_tile.get_atlas_coords(), hovered_tile.alternative_tile));
2114 } else {
2115 tile_set_selection.insert(TileMapCell(source_id, hovered_tile.get_atlas_coords(), hovered_tile.alternative_tile));
2116 }
2117 }
2118 _update_selection_pattern_from_tileset_tiles_selection();
2119 }
2120 tile_atlas_control->queue_redraw();
2121 alternative_tiles_control->queue_redraw();
2122 }
2123}
2124
2125void TileMapEditorTilesPlugin::_set_tile_map_selection(const TypedArray<Vector2i> &p_selection) {
2126 tile_map_selection.clear();
2127 for (int i = 0; i < p_selection.size(); i++) {
2128 tile_map_selection.insert(p_selection[i]);
2129 }
2130 _update_selection_pattern_from_tilemap_selection();
2131 _update_tileset_selection_from_selection_pattern();
2132 CanvasItemEditor::get_singleton()->update_viewport();
2133}
2134
2135TypedArray<Vector2i> TileMapEditorTilesPlugin::_get_tile_map_selection() const {
2136 TypedArray<Vector2i> output;
2137 for (const Vector2i &E : tile_map_selection) {
2138 output.push_back(E);
2139 }
2140 return output;
2141}
2142
2143void TileMapEditorTilesPlugin::edit(ObjectID p_tile_map_id, int p_tile_map_layer) {
2144 _stop_dragging(); // Avoids staying in a wrong drag state.
2145
2146 // Disable sort button if the tileset is read-only
2147 TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
2148 if (tile_map) {
2149 Ref<TileSet> tile_set = tile_map->get_tileset();
2150 if (tile_set.is_valid()) {
2151 source_sort_button->set_disabled(EditorNode::get_singleton()->is_resource_read_only(tile_set));
2152 }
2153 }
2154
2155 if (tile_map_id != p_tile_map_id) {
2156 tile_map_id = p_tile_map_id;
2157
2158 // Clear the selection.
2159 tile_set_selection.clear();
2160 patterns_item_list->deselect_all();
2161 tile_map_selection.clear();
2162 selection_pattern.instantiate();
2163 }
2164
2165 tile_map_layer = p_tile_map_layer;
2166}
2167
2168void TileMapEditorTilesPlugin::_set_source_sort(int p_sort) {
2169 for (int i = 0; i != TilesEditorUtils::SOURCE_SORT_MAX; i++) {
2170 source_sort_button->get_popup()->set_item_checked(i, (i == (int)p_sort));
2171 }
2172 TilesEditorUtils::get_singleton()->set_sorting_option(p_sort);
2173 _update_tile_set_sources_list();
2174 EditorSettings::get_singleton()->set_project_metadata("editor_metadata", "tile_source_sort", p_sort);
2175}
2176
2177void TileMapEditorTilesPlugin::_bind_methods() {
2178 ClassDB::bind_method(D_METHOD("_scene_thumbnail_done"), &TileMapEditorTilesPlugin::_scene_thumbnail_done);
2179 ClassDB::bind_method(D_METHOD("_set_tile_map_selection", "selection"), &TileMapEditorTilesPlugin::_set_tile_map_selection);
2180 ClassDB::bind_method(D_METHOD("_get_tile_map_selection"), &TileMapEditorTilesPlugin::_get_tile_map_selection);
2181}
2182
2183TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() {
2184 CanvasItemEditor::get_singleton()
2185 ->get_viewport_control()
2186 ->connect("mouse_exited", callable_mp(this, &TileMapEditorTilesPlugin::_mouse_exited_viewport));
2187
2188 // --- Shortcuts ---
2189 ED_SHORTCUT("tiles_editor/cut", TTR("Cut"), KeyModifierMask::CMD_OR_CTRL | Key::X);
2190 ED_SHORTCUT("tiles_editor/copy", TTR("Copy"), KeyModifierMask::CMD_OR_CTRL | Key::C);
2191 ED_SHORTCUT("tiles_editor/paste", TTR("Paste"), KeyModifierMask::CMD_OR_CTRL | Key::V);
2192 ED_SHORTCUT("tiles_editor/cancel", TTR("Cancel"), Key::ESCAPE);
2193 ED_SHORTCUT("tiles_editor/delete", TTR("Delete"), Key::KEY_DELETE);
2194
2195 // --- Initialize references ---
2196 tile_map_clipboard.instantiate();
2197 selection_pattern.instantiate();
2198
2199 // --- Toolbar ---
2200 toolbar = memnew(HBoxContainer);
2201
2202 HBoxContainer *tilemap_tiles_tools_buttons = memnew(HBoxContainer);
2203
2204 tool_buttons_group.instantiate();
2205
2206 select_tool_button = memnew(Button);
2207 select_tool_button->set_flat(true);
2208 select_tool_button->set_toggle_mode(true);
2209 select_tool_button->set_button_group(tool_buttons_group);
2210 select_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/selection_tool", TTR("Selection"), Key::S));
2211 select_tool_button->connect("pressed", callable_mp(this, &TileMapEditorTilesPlugin::_update_toolbar));
2212 tilemap_tiles_tools_buttons->add_child(select_tool_button);
2213 viewport_shortcut_buttons.push_back(select_tool_button);
2214
2215 paint_tool_button = memnew(Button);
2216 paint_tool_button->set_flat(true);
2217 paint_tool_button->set_toggle_mode(true);
2218 paint_tool_button->set_button_group(tool_buttons_group);
2219 paint_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/paint_tool", TTR("Paint"), Key::D));
2220 paint_tool_button->set_tooltip_text(TTR("Shift: Draw line.") + "\n" + TTR("Shift+Ctrl: Draw rectangle."));
2221 paint_tool_button->connect("pressed", callable_mp(this, &TileMapEditorTilesPlugin::_update_toolbar));
2222 tilemap_tiles_tools_buttons->add_child(paint_tool_button);
2223 viewport_shortcut_buttons.push_back(paint_tool_button);
2224
2225 line_tool_button = memnew(Button);
2226 line_tool_button->set_flat(true);
2227 line_tool_button->set_toggle_mode(true);
2228 line_tool_button->set_button_group(tool_buttons_group);
2229 // TRANSLATORS: This refers to the line tool in the tilemap editor.
2230 line_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/line_tool", TTR("Line", "Tool"), Key::L));
2231 line_tool_button->connect("pressed", callable_mp(this, &TileMapEditorTilesPlugin::_update_toolbar));
2232 tilemap_tiles_tools_buttons->add_child(line_tool_button);
2233 viewport_shortcut_buttons.push_back(line_tool_button);
2234
2235 rect_tool_button = memnew(Button);
2236 rect_tool_button->set_flat(true);
2237 rect_tool_button->set_toggle_mode(true);
2238 rect_tool_button->set_button_group(tool_buttons_group);
2239 rect_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/rect_tool", TTR("Rect"), Key::R));
2240 rect_tool_button->connect("pressed", callable_mp(this, &TileMapEditorTilesPlugin::_update_toolbar));
2241 tilemap_tiles_tools_buttons->add_child(rect_tool_button);
2242 viewport_shortcut_buttons.push_back(rect_tool_button);
2243
2244 bucket_tool_button = memnew(Button);
2245 bucket_tool_button->set_flat(true);
2246 bucket_tool_button->set_toggle_mode(true);
2247 bucket_tool_button->set_button_group(tool_buttons_group);
2248 bucket_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/bucket_tool", TTR("Bucket"), Key::B));
2249 bucket_tool_button->connect("pressed", callable_mp(this, &TileMapEditorTilesPlugin::_update_toolbar));
2250 tilemap_tiles_tools_buttons->add_child(bucket_tool_button);
2251 toolbar->add_child(tilemap_tiles_tools_buttons);
2252 viewport_shortcut_buttons.push_back(bucket_tool_button);
2253
2254 // -- TileMap tool settings --
2255 tools_settings = memnew(HBoxContainer);
2256 toolbar->add_child(tools_settings);
2257
2258 tools_settings_vsep = memnew(VSeparator);
2259 tools_settings->add_child(tools_settings_vsep);
2260
2261 // Picker
2262 picker_button = memnew(Button);
2263 picker_button->set_flat(true);
2264 picker_button->set_toggle_mode(true);
2265 picker_button->set_shortcut(ED_SHORTCUT("tiles_editor/picker", TTR("Picker"), Key::P));
2266 picker_button->set_tooltip_text(TTR("Alternatively hold Ctrl with other tools to pick tile."));
2267 picker_button->connect("pressed", callable_mp(CanvasItemEditor::get_singleton(), &CanvasItemEditor::update_viewport));
2268 tools_settings->add_child(picker_button);
2269 viewport_shortcut_buttons.push_back(picker_button);
2270
2271 // Erase button.
2272 erase_button = memnew(Button);
2273 erase_button->set_flat(true);
2274 erase_button->set_toggle_mode(true);
2275 erase_button->set_shortcut(ED_SHORTCUT("tiles_editor/eraser", TTR("Eraser"), Key::E));
2276 erase_button->set_tooltip_text(TTR("Alternatively use RMB to erase tiles."));
2277 erase_button->connect("pressed", callable_mp(CanvasItemEditor::get_singleton(), &CanvasItemEditor::update_viewport));
2278 tools_settings->add_child(erase_button);
2279 viewport_shortcut_buttons.push_back(erase_button);
2280
2281 // Transform toolbar.
2282 transform_toolbar = memnew(HBoxContainer);
2283 tools_settings->add_child(transform_toolbar);
2284 transform_toolbar->add_child(memnew(VSeparator));
2285
2286 transform_button_rotate_left = memnew(Button);
2287 transform_button_rotate_left->set_flat(true);
2288 transform_button_rotate_left->set_shortcut(ED_SHORTCUT("tiles_editor/rotate_tile_left", TTR("Rotate Tile Left"), Key::Z));
2289 transform_toolbar->add_child(transform_button_rotate_left);
2290 transform_button_rotate_left->connect("pressed", callable_mp(this, &TileMapEditorTilesPlugin::_apply_transform).bind(TRANSFORM_ROTATE_LEFT));
2291 viewport_shortcut_buttons.push_back(transform_button_rotate_left);
2292
2293 transform_button_rotate_right = memnew(Button);
2294 transform_button_rotate_right->set_flat(true);
2295 transform_button_rotate_right->set_shortcut(ED_SHORTCUT("tiles_editor/rotate_tile_right", TTR("Rotate Tile Right"), Key::X));
2296 transform_toolbar->add_child(transform_button_rotate_right);
2297 transform_button_rotate_right->connect("pressed", callable_mp(this, &TileMapEditorTilesPlugin::_apply_transform).bind(TRANSFORM_ROTATE_RIGHT));
2298 viewport_shortcut_buttons.push_back(transform_button_rotate_right);
2299
2300 transform_button_flip_h = memnew(Button);
2301 transform_button_flip_h->set_flat(true);
2302 transform_button_flip_h->set_shortcut(ED_SHORTCUT("tiles_editor/flip_tile_horizontal", TTR("Flip Tile Horizontally"), Key::C));
2303 transform_toolbar->add_child(transform_button_flip_h);
2304 transform_button_flip_h->connect("pressed", callable_mp(this, &TileMapEditorTilesPlugin::_apply_transform).bind(TRANSFORM_FLIP_H));
2305 viewport_shortcut_buttons.push_back(transform_button_flip_h);
2306
2307 transform_button_flip_v = memnew(Button);
2308 transform_button_flip_v->set_flat(true);
2309 transform_button_flip_v->set_shortcut(ED_SHORTCUT("tiles_editor/flip_tile_vertical", TTR("Flip Tile Vertically"), Key::V));
2310 transform_toolbar->add_child(transform_button_flip_v);
2311 transform_button_flip_v->connect("pressed", callable_mp(this, &TileMapEditorTilesPlugin::_apply_transform).bind(TRANSFORM_FLIP_V));
2312 viewport_shortcut_buttons.push_back(transform_button_flip_v);
2313
2314 // Separator 2.
2315 tools_settings_vsep_2 = memnew(VSeparator);
2316 tools_settings->add_child(tools_settings_vsep_2);
2317
2318 // Continuous checkbox.
2319 bucket_contiguous_checkbox = memnew(CheckBox);
2320 bucket_contiguous_checkbox->set_flat(true);
2321 bucket_contiguous_checkbox->set_text(TTR("Contiguous"));
2322 bucket_contiguous_checkbox->set_pressed(true);
2323 tools_settings->add_child(bucket_contiguous_checkbox);
2324
2325 // Random tile checkbox.
2326 random_tile_toggle = memnew(Button);
2327 random_tile_toggle->set_flat(true);
2328 random_tile_toggle->set_toggle_mode(true);
2329 random_tile_toggle->set_tooltip_text(TTR("Place Random Tile"));
2330 random_tile_toggle->connect("toggled", callable_mp(this, &TileMapEditorTilesPlugin::_on_random_tile_checkbox_toggled));
2331 tools_settings->add_child(random_tile_toggle);
2332
2333 // Random tile scattering.
2334 scatter_controls_container = memnew(HBoxContainer);
2335
2336 scatter_label = memnew(Label);
2337 scatter_label->set_tooltip_text(TTR("Modifies the chance of painting nothing instead of a randomly selected tile."));
2338 scatter_label->set_text(TTR("Scattering:"));
2339 scatter_controls_container->add_child(scatter_label);
2340
2341 scatter_spinbox = memnew(SpinBox);
2342 scatter_spinbox->set_min(0.0);
2343 scatter_spinbox->set_max(1000);
2344 scatter_spinbox->set_step(0.001);
2345 scatter_spinbox->set_tooltip_text(TTR("Modifies the chance of painting nothing instead of a randomly selected tile."));
2346 scatter_spinbox->get_line_edit()->add_theme_constant_override("minimum_character_width", 4);
2347 scatter_spinbox->connect("value_changed", callable_mp(this, &TileMapEditorTilesPlugin::_on_scattering_spinbox_changed));
2348 scatter_controls_container->add_child(scatter_spinbox);
2349 tools_settings->add_child(scatter_controls_container);
2350
2351 _on_random_tile_checkbox_toggled(false);
2352
2353 // Default tool.
2354 paint_tool_button->set_pressed(true);
2355 _update_toolbar();
2356
2357 // --- Bottom panel tiles ---
2358 tiles_bottom_panel = memnew(VBoxContainer);
2359 tiles_bottom_panel->connect("tree_entered", callable_mp(this, &TileMapEditorTilesPlugin::_update_theme));
2360 tiles_bottom_panel->connect("theme_changed", callable_mp(this, &TileMapEditorTilesPlugin::_update_theme));
2361 tiles_bottom_panel->connect("visibility_changed", callable_mp(this, &TileMapEditorTilesPlugin::_stop_dragging));
2362 tiles_bottom_panel->connect("visibility_changed", callable_mp(this, &TileMapEditorTilesPlugin::_tab_changed));
2363 tiles_bottom_panel->set_name(TTR("Tiles"));
2364
2365 missing_source_label = memnew(Label);
2366 missing_source_label->set_text(TTR("This TileMap's TileSet has no source configured. Go to the TileSet bottom tab to add one."));
2367 missing_source_label->set_h_size_flags(Control::SIZE_EXPAND_FILL);
2368 missing_source_label->set_v_size_flags(Control::SIZE_EXPAND_FILL);
2369 missing_source_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
2370 missing_source_label->set_vertical_alignment(VERTICAL_ALIGNMENT_CENTER);
2371 missing_source_label->hide();
2372 tiles_bottom_panel->add_child(missing_source_label);
2373
2374 atlas_sources_split_container = memnew(HSplitContainer);
2375 atlas_sources_split_container->set_h_size_flags(Control::SIZE_EXPAND_FILL);
2376 atlas_sources_split_container->set_v_size_flags(Control::SIZE_EXPAND_FILL);
2377 tiles_bottom_panel->add_child(atlas_sources_split_container);
2378
2379 VBoxContainer *split_container_left_side = memnew(VBoxContainer);
2380 split_container_left_side->set_h_size_flags(Control::SIZE_EXPAND_FILL);
2381 split_container_left_side->set_v_size_flags(Control::SIZE_EXPAND_FILL);
2382 split_container_left_side->set_stretch_ratio(0.25);
2383 split_container_left_side->set_custom_minimum_size(Size2(70, 0) * EDSCALE);
2384 atlas_sources_split_container->add_child(split_container_left_side);
2385
2386 HBoxContainer *sources_bottom_actions = memnew(HBoxContainer);
2387 sources_bottom_actions->set_alignment(HBoxContainer::ALIGNMENT_END);
2388
2389 source_sort_button = memnew(MenuButton);
2390 source_sort_button->set_flat(true);
2391 source_sort_button->set_tooltip_text(TTR("Sort sources"));
2392
2393 PopupMenu *p = source_sort_button->get_popup();
2394 p->connect("id_pressed", callable_mp(this, &TileMapEditorTilesPlugin::_set_source_sort));
2395 p->add_radio_check_item(TTR("Sort by ID (Ascending)"), TilesEditorUtils::SOURCE_SORT_ID);
2396 p->add_radio_check_item(TTR("Sort by ID (Descending)"), TilesEditorUtils::SOURCE_SORT_ID_REVERSE);
2397 p->add_radio_check_item(TTR("Sort by Name (Ascending)"), TilesEditorUtils::SOURCE_SORT_NAME);
2398 p->add_radio_check_item(TTR("Sort by Name (Descending)"), TilesEditorUtils::SOURCE_SORT_NAME_REVERSE);
2399 p->set_item_checked(TilesEditorUtils::SOURCE_SORT_ID, true);
2400 sources_bottom_actions->add_child(source_sort_button);
2401
2402 sources_list = memnew(ItemList);
2403 sources_list->set_fixed_icon_size(Size2(60, 60) * EDSCALE);
2404 sources_list->set_h_size_flags(Control::SIZE_EXPAND_FILL);
2405 sources_list->set_v_size_flags(Control::SIZE_EXPAND_FILL);
2406 sources_list->set_stretch_ratio(0.25);
2407 sources_list->set_custom_minimum_size(Size2(70, 0) * EDSCALE);
2408 sources_list->set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST);
2409 sources_list->connect("item_selected", callable_mp(this, &TileMapEditorTilesPlugin::_update_fix_selected_and_hovered).unbind(1));
2410 sources_list->connect("item_selected", callable_mp(this, &TileMapEditorTilesPlugin::_update_source_display).unbind(1));
2411 sources_list->connect("item_selected", callable_mp(TilesEditorUtils::get_singleton(), &TilesEditorUtils::set_sources_lists_current));
2412 sources_list->connect("item_activated", callable_mp(TilesEditorUtils::get_singleton(), &TilesEditorUtils::display_tile_set_editor_panel).unbind(1));
2413 sources_list->connect("visibility_changed", callable_mp(TilesEditorUtils::get_singleton(), &TilesEditorUtils::synchronize_sources_list).bind(sources_list, source_sort_button));
2414 sources_list->add_user_signal(MethodInfo("sort_request"));
2415 sources_list->connect("sort_request", callable_mp(this, &TileMapEditorTilesPlugin::_update_tile_set_sources_list));
2416 split_container_left_side->add_child(sources_list);
2417 split_container_left_side->add_child(sources_bottom_actions);
2418
2419 // Tile atlas source.
2420 tile_atlas_view = memnew(TileAtlasView);
2421 tile_atlas_view->set_h_size_flags(Control::SIZE_EXPAND_FILL);
2422 tile_atlas_view->set_v_size_flags(Control::SIZE_EXPAND_FILL);
2423 tile_atlas_view->set_texture_grid_visible(false);
2424 tile_atlas_view->set_tile_shape_grid_visible(false);
2425 tile_atlas_view->connect("transform_changed", callable_mp(TilesEditorUtils::get_singleton(), &TilesEditorUtils::set_atlas_view_transform));
2426 atlas_sources_split_container->add_child(tile_atlas_view);
2427
2428 tile_atlas_control = memnew(Control);
2429 tile_atlas_control->connect("draw", callable_mp(this, &TileMapEditorTilesPlugin::_tile_atlas_control_draw));
2430 tile_atlas_control->connect("mouse_exited", callable_mp(this, &TileMapEditorTilesPlugin::_tile_atlas_control_mouse_exited));
2431 tile_atlas_control->connect("gui_input", callable_mp(this, &TileMapEditorTilesPlugin::_tile_atlas_control_gui_input));
2432 tile_atlas_view->add_control_over_atlas_tiles(tile_atlas_control);
2433
2434 alternative_tiles_control = memnew(Control);
2435 alternative_tiles_control->connect("draw", callable_mp(this, &TileMapEditorTilesPlugin::_tile_alternatives_control_draw));
2436 alternative_tiles_control->connect("mouse_exited", callable_mp(this, &TileMapEditorTilesPlugin::_tile_alternatives_control_mouse_exited));
2437 alternative_tiles_control->connect("gui_input", callable_mp(this, &TileMapEditorTilesPlugin::_tile_alternatives_control_gui_input));
2438 tile_atlas_view->add_control_over_alternative_tiles(alternative_tiles_control);
2439
2440 // Scenes collection source.
2441 scene_tiles_list = memnew(ItemList);
2442 scene_tiles_list->set_h_size_flags(Control::SIZE_EXPAND_FILL);
2443 scene_tiles_list->set_v_size_flags(Control::SIZE_EXPAND_FILL);
2444 scene_tiles_list->set_select_mode(ItemList::SELECT_MULTI);
2445 scene_tiles_list->connect("multi_selected", callable_mp(this, &TileMapEditorTilesPlugin::_scenes_list_multi_selected));
2446 scene_tiles_list->connect("empty_clicked", callable_mp(this, &TileMapEditorTilesPlugin::_scenes_list_lmb_empty_clicked));
2447 scene_tiles_list->set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST);
2448 atlas_sources_split_container->add_child(scene_tiles_list);
2449
2450 // Invalid source label.
2451 invalid_source_label = memnew(Label);
2452 invalid_source_label->set_text(TTR("Invalid source selected."));
2453 invalid_source_label->set_h_size_flags(Control::SIZE_EXPAND_FILL);
2454 invalid_source_label->set_v_size_flags(Control::SIZE_EXPAND_FILL);
2455 invalid_source_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
2456 invalid_source_label->set_vertical_alignment(VERTICAL_ALIGNMENT_CENTER);
2457 invalid_source_label->hide();
2458 atlas_sources_split_container->add_child(invalid_source_label);
2459
2460 // --- Bottom panel patterns ---
2461 patterns_bottom_panel = memnew(VBoxContainer);
2462 patterns_bottom_panel->set_name(TTR("Patterns"));
2463 patterns_bottom_panel->connect("visibility_changed", callable_mp(this, &TileMapEditorTilesPlugin::_tab_changed));
2464
2465 int thumbnail_size = 64;
2466 patterns_item_list = memnew(ItemList);
2467 patterns_item_list->set_max_columns(0);
2468 patterns_item_list->set_icon_mode(ItemList::ICON_MODE_TOP);
2469 patterns_item_list->set_fixed_column_width(thumbnail_size * 3 / 2);
2470 patterns_item_list->set_max_text_lines(2);
2471 patterns_item_list->set_fixed_icon_size(Size2(thumbnail_size, thumbnail_size));
2472 patterns_item_list->set_v_size_flags(Control::SIZE_EXPAND_FILL);
2473 patterns_item_list->connect("gui_input", callable_mp(this, &TileMapEditorTilesPlugin::_patterns_item_list_gui_input));
2474 patterns_item_list->connect("item_selected", callable_mp(this, &TileMapEditorTilesPlugin::_update_selection_pattern_from_tileset_pattern_selection).unbind(1));
2475 patterns_item_list->connect("item_activated", callable_mp(this, &TileMapEditorTilesPlugin::_update_selection_pattern_from_tileset_pattern_selection).unbind(1));
2476 patterns_item_list->connect("empty_clicked", callable_mp(this, &TileMapEditorTilesPlugin::patterns_item_list_empty_clicked));
2477 patterns_bottom_panel->add_child(patterns_item_list);
2478
2479 patterns_help_label = memnew(Label);
2480 patterns_help_label->set_text(TTR("Drag and drop or paste a TileMap selection here to store a pattern."));
2481 patterns_help_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
2482 patterns_help_label->set_anchors_and_offsets_preset(Control::PRESET_CENTER);
2483 patterns_item_list->add_child(patterns_help_label);
2484
2485 // Update.
2486 _update_source_display();
2487}
2488
2489TileMapEditorTilesPlugin::~TileMapEditorTilesPlugin() {
2490}
2491
2492void TileMapEditorTerrainsPlugin::tile_set_changed() {
2493 _update_terrains_cache();
2494 _update_terrains_tree();
2495 _update_tiles_list();
2496}
2497
2498void TileMapEditorTerrainsPlugin::_update_toolbar() {
2499 // Hide all settings.
2500 for (int i = 0; i < tools_settings->get_child_count(); i++) {
2501 Object::cast_to<CanvasItem>(tools_settings->get_child(i))->hide();
2502 }
2503
2504 // Show only the correct settings.
2505 if (tool_buttons_group->get_pressed_button() != bucket_tool_button) {
2506 tools_settings_vsep->show();
2507 picker_button->show();
2508 erase_button->show();
2509 } else {
2510 tools_settings_vsep->show();
2511 picker_button->show();
2512 erase_button->show();
2513 tools_settings_vsep_2->show();
2514 bucket_contiguous_checkbox->show();
2515 }
2516}
2517
2518Vector<TileMapSubEditorPlugin::TabData> TileMapEditorTerrainsPlugin::get_tabs() const {
2519 Vector<TileMapSubEditorPlugin::TabData> tabs;
2520 tabs.push_back({ toolbar, main_vbox_container });
2521 return tabs;
2522}
2523
2524HashMap<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_terrain_path_or_connect(const Vector<Vector2i> &p_to_paint, int p_terrain_set, int p_terrain, bool p_connect) const {
2525 TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
2526 if (!tile_map) {
2527 return HashMap<Vector2i, TileMapCell>();
2528 }
2529
2530 Ref<TileSet> tile_set = tile_map->get_tileset();
2531 if (!tile_set.is_valid()) {
2532 return HashMap<Vector2i, TileMapCell>();
2533 }
2534
2535 HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_output;
2536 if (p_connect) {
2537 terrain_fill_output = tile_map->terrain_fill_connect(tile_map_layer, p_to_paint, p_terrain_set, p_terrain, false);
2538 } else {
2539 terrain_fill_output = tile_map->terrain_fill_path(tile_map_layer, p_to_paint, p_terrain_set, p_terrain, false);
2540 }
2541
2542 // Make the painted path a set for faster lookups
2543 HashSet<Vector2i> painted_set;
2544 for (Vector2i coords : p_to_paint) {
2545 painted_set.insert(coords);
2546 }
2547
2548 HashMap<Vector2i, TileMapCell> output;
2549 for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &kv : terrain_fill_output) {
2550 if (painted_set.has(kv.key)) {
2551 // Paint a random tile with the correct terrain for the painted path.
2552 output[kv.key] = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, kv.value);
2553 } else {
2554 // Avoids updating the painted path from the output if the new pattern is the same as before.
2555 TileSet::TerrainsPattern in_map_terrain_pattern = TileSet::TerrainsPattern(*tile_set, p_terrain_set);
2556 TileMapCell cell = tile_map->get_cell(tile_map_layer, kv.key);
2557 if (cell.source_id != TileSet::INVALID_SOURCE) {
2558 TileSetSource *source = *tile_set->get_source(cell.source_id);
2559 TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
2560 if (atlas_source) {
2561 // Get tile data.
2562 TileData *tile_data = atlas_source->get_tile_data(cell.get_atlas_coords(), cell.alternative_tile);
2563 if (tile_data && tile_data->get_terrain_set() == p_terrain_set) {
2564 in_map_terrain_pattern = tile_data->get_terrains_pattern();
2565 }
2566 }
2567 }
2568 if (in_map_terrain_pattern != kv.value) {
2569 output[kv.key] = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, kv.value);
2570 }
2571 }
2572 }
2573 return output;
2574}
2575
2576HashMap<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_terrain_pattern(const Vector<Vector2i> &p_to_paint, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern) const {
2577 TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
2578 if (!tile_map) {
2579 return HashMap<Vector2i, TileMapCell>();
2580 }
2581
2582 Ref<TileSet> tile_set = tile_map->get_tileset();
2583 if (!tile_set.is_valid()) {
2584 return HashMap<Vector2i, TileMapCell>();
2585 }
2586
2587 HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_output = tile_map->terrain_fill_pattern(tile_map_layer, p_to_paint, p_terrain_set, p_terrains_pattern, false);
2588
2589 // Make the painted path a set for faster lookups
2590 HashSet<Vector2i> painted_set;
2591 for (Vector2i coords : p_to_paint) {
2592 painted_set.insert(coords);
2593 }
2594
2595 HashMap<Vector2i, TileMapCell> output;
2596 for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &kv : terrain_fill_output) {
2597 if (painted_set.has(kv.key)) {
2598 // Paint a random tile with the correct terrain for the painted path.
2599 output[kv.key] = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, kv.value);
2600 } else {
2601 // Avoids updating the painted path from the output if the new pattern is the same as before.
2602 TileSet::TerrainsPattern in_map_terrain_pattern = TileSet::TerrainsPattern(*tile_set, p_terrain_set);
2603 TileMapCell cell = tile_map->get_cell(tile_map_layer, kv.key);
2604 if (cell.source_id != TileSet::INVALID_SOURCE) {
2605 TileSetSource *source = *tile_set->get_source(cell.source_id);
2606 TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
2607 if (atlas_source) {
2608 // Get tile data.
2609 TileData *tile_data = atlas_source->get_tile_data(cell.get_atlas_coords(), cell.alternative_tile);
2610 if (tile_data && tile_data->get_terrain_set() == p_terrain_set) {
2611 in_map_terrain_pattern = tile_data->get_terrains_pattern();
2612 }
2613 }
2614 }
2615 if (in_map_terrain_pattern != kv.value) {
2616 output[kv.key] = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, kv.value);
2617 }
2618 }
2619 }
2620 return output;
2621}
2622
2623HashMap<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_line(Vector2i p_start_cell, Vector2i p_end_cell, bool p_erase) {
2624 TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
2625 if (!tile_map) {
2626 return HashMap<Vector2i, TileMapCell>();
2627 }
2628
2629 Ref<TileSet> tile_set = tile_map->get_tileset();
2630 if (!tile_set.is_valid()) {
2631 return HashMap<Vector2i, TileMapCell>();
2632 }
2633
2634 if (p_erase) {
2635 return _draw_terrain_pattern(TileMapEditor::get_line(tile_map, p_start_cell, p_end_cell), selected_terrain_set, TileSet::TerrainsPattern(*tile_set, selected_terrain_set));
2636 } else {
2637 if (selected_type == SELECTED_TYPE_CONNECT) {
2638 return _draw_terrain_path_or_connect(TileMapEditor::get_line(tile_map, p_start_cell, p_end_cell), selected_terrain_set, selected_terrain, true);
2639 } else if (selected_type == SELECTED_TYPE_PATH) {
2640 return _draw_terrain_path_or_connect(TileMapEditor::get_line(tile_map, p_start_cell, p_end_cell), selected_terrain_set, selected_terrain, false);
2641 } else { // SELECTED_TYPE_PATTERN
2642 return _draw_terrain_pattern(TileMapEditor::get_line(tile_map, p_start_cell, p_end_cell), selected_terrain_set, selected_terrains_pattern);
2643 }
2644 }
2645}
2646
2647HashMap<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_rect(Vector2i p_start_cell, Vector2i p_end_cell, bool p_erase) {
2648 TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
2649 if (!tile_map) {
2650 return HashMap<Vector2i, TileMapCell>();
2651 }
2652
2653 Ref<TileSet> tile_set = tile_map->get_tileset();
2654 if (!tile_set.is_valid()) {
2655 return HashMap<Vector2i, TileMapCell>();
2656 }
2657
2658 Rect2i rect;
2659 rect.set_position(p_start_cell);
2660 rect.set_end(p_end_cell);
2661 rect = rect.abs();
2662
2663 Vector<Vector2i> to_draw;
2664 for (int x = rect.position.x; x <= rect.get_end().x; x++) {
2665 for (int y = rect.position.y; y <= rect.get_end().y; y++) {
2666 to_draw.append(Vector2i(x, y));
2667 }
2668 }
2669
2670 if (p_erase) {
2671 return _draw_terrain_pattern(to_draw, selected_terrain_set, TileSet::TerrainsPattern(*tile_set, selected_terrain_set));
2672 } else {
2673 if (selected_type == SELECTED_TYPE_CONNECT || selected_type == SELECTED_TYPE_PATH) {
2674 return _draw_terrain_path_or_connect(to_draw, selected_terrain_set, selected_terrain, true);
2675 } else { // SELECTED_TYPE_PATTERN
2676 return _draw_terrain_pattern(to_draw, selected_terrain_set, selected_terrains_pattern);
2677 }
2678 }
2679}
2680
2681RBSet<Vector2i> TileMapEditorTerrainsPlugin::_get_cells_for_bucket_fill(Vector2i p_coords, bool p_contiguous) {
2682 TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
2683 if (!tile_map) {
2684 return RBSet<Vector2i>();
2685 }
2686
2687 Ref<TileSet> tile_set = tile_map->get_tileset();
2688 if (!tile_set.is_valid()) {
2689 return RBSet<Vector2i>();
2690 }
2691
2692 TileMapCell source_cell = tile_map->get_cell(tile_map_layer, p_coords);
2693
2694 TileSet::TerrainsPattern source_pattern(*tile_set, selected_terrain_set);
2695 if (source_cell.source_id != TileSet::INVALID_SOURCE) {
2696 TileData *tile_data = nullptr;
2697 Ref<TileSetSource> source = tile_set->get_source(source_cell.source_id);
2698 Ref<TileSetAtlasSource> atlas_source = source;
2699 if (atlas_source.is_valid()) {
2700 tile_data = atlas_source->get_tile_data(source_cell.get_atlas_coords(), source_cell.alternative_tile);
2701 }
2702 if (!tile_data) {
2703 return RBSet<Vector2i>();
2704 }
2705 source_pattern = tile_data->get_terrains_pattern();
2706 }
2707
2708 // If we are filling empty tiles, compute the tilemap boundaries.
2709 Rect2i boundaries;
2710 if (source_cell.source_id == TileSet::INVALID_SOURCE) {
2711 boundaries = tile_map->get_used_rect();
2712 }
2713
2714 RBSet<Vector2i> output;
2715 if (p_contiguous) {
2716 // Replace continuous tiles like the source.
2717 RBSet<Vector2i> already_checked;
2718 List<Vector2i> to_check;
2719 to_check.push_back(p_coords);
2720 while (!to_check.is_empty()) {
2721 Vector2i coords = to_check.back()->get();
2722 to_check.pop_back();
2723 if (!already_checked.has(coords)) {
2724 // Get the candidate cell pattern.
2725 TileSet::TerrainsPattern candidate_pattern(*tile_set, selected_terrain_set);
2726 if (tile_map->get_cell_source_id(tile_map_layer, coords) != TileSet::INVALID_SOURCE) {
2727 TileData *tile_data = nullptr;
2728 Ref<TileSetSource> source = tile_set->get_source(tile_map->get_cell_source_id(tile_map_layer, coords));
2729 Ref<TileSetAtlasSource> atlas_source = source;
2730 if (atlas_source.is_valid()) {
2731 tile_data = atlas_source->get_tile_data(tile_map->get_cell_atlas_coords(tile_map_layer, coords), tile_map->get_cell_alternative_tile(tile_map_layer, coords));
2732 }
2733 if (tile_data) {
2734 candidate_pattern = tile_data->get_terrains_pattern();
2735 }
2736 }
2737
2738 // Draw.
2739 if (candidate_pattern == source_pattern && (!source_pattern.is_erase_pattern() || boundaries.has_point(coords))) {
2740 output.insert(coords);
2741
2742 // Get surrounding tiles (handles different tile shapes).
2743 TypedArray<Vector2i> around = tile_map->get_surrounding_cells(coords);
2744 for (int i = 0; i < around.size(); i++) {
2745 to_check.push_back(around[i]);
2746 }
2747 }
2748 already_checked.insert(coords);
2749 }
2750 }
2751 } else {
2752 // Replace all tiles like the source.
2753 TypedArray<Vector2i> to_check;
2754 if (source_cell.source_id == TileSet::INVALID_SOURCE) {
2755 Rect2i rect = tile_map->get_used_rect();
2756 if (!rect.has_area()) {
2757 rect = Rect2i(p_coords, Vector2i(1, 1));
2758 }
2759 for (int x = boundaries.position.x; x < boundaries.get_end().x; x++) {
2760 for (int y = boundaries.position.y; y < boundaries.get_end().y; y++) {
2761 to_check.append(Vector2i(x, y));
2762 }
2763 }
2764 } else {
2765 to_check = tile_map->get_used_cells(tile_map_layer);
2766 }
2767 for (int i = 0; i < to_check.size(); i++) {
2768 Vector2i coords = to_check[i];
2769 // Get the candidate cell pattern.
2770 TileSet::TerrainsPattern candidate_pattern;
2771 if (tile_map->get_cell_source_id(tile_map_layer, coords) != TileSet::INVALID_SOURCE) {
2772 TileData *tile_data = nullptr;
2773 Ref<TileSetSource> source = tile_set->get_source(tile_map->get_cell_source_id(tile_map_layer, coords));
2774 Ref<TileSetAtlasSource> atlas_source = source;
2775 if (atlas_source.is_valid()) {
2776 tile_data = atlas_source->get_tile_data(tile_map->get_cell_atlas_coords(tile_map_layer, coords), tile_map->get_cell_alternative_tile(tile_map_layer, coords));
2777 }
2778 if (tile_data) {
2779 candidate_pattern = tile_data->get_terrains_pattern();
2780 }
2781 }
2782
2783 // Draw.
2784 if (candidate_pattern == source_pattern && (!source_pattern.is_erase_pattern() || boundaries.has_point(coords))) {
2785 output.insert(coords);
2786 }
2787 }
2788 }
2789 return output;
2790}
2791
2792HashMap<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_bucket_fill(Vector2i p_coords, bool p_contiguous, bool p_erase) {
2793 TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
2794 if (!tile_map) {
2795 return HashMap<Vector2i, TileMapCell>();
2796 }
2797
2798 Ref<TileSet> tile_set = tile_map->get_tileset();
2799 if (!tile_set.is_valid()) {
2800 return HashMap<Vector2i, TileMapCell>();
2801 }
2802
2803 RBSet<Vector2i> cells_to_draw = _get_cells_for_bucket_fill(p_coords, p_contiguous);
2804 Vector<Vector2i> cells_to_draw_as_vector;
2805 for (Vector2i cell : cells_to_draw) {
2806 cells_to_draw_as_vector.append(cell);
2807 }
2808
2809 if (p_erase) {
2810 return _draw_terrain_pattern(cells_to_draw_as_vector, selected_terrain_set, TileSet::TerrainsPattern(*tile_set, selected_terrain_set));
2811 } else {
2812 if (selected_type == SELECTED_TYPE_CONNECT || selected_type == SELECTED_TYPE_PATH) {
2813 return _draw_terrain_path_or_connect(cells_to_draw_as_vector, selected_terrain_set, selected_terrain, true);
2814 } else { // SELECTED_TYPE_PATTERN
2815 return _draw_terrain_pattern(cells_to_draw_as_vector, selected_terrain_set, selected_terrains_pattern);
2816 }
2817 }
2818}
2819
2820void TileMapEditorTerrainsPlugin::_stop_dragging() {
2821 TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
2822 if (!tile_map) {
2823 return;
2824 }
2825
2826 Ref<TileSet> tile_set = tile_map->get_tileset();
2827 if (!tile_set.is_valid()) {
2828 return;
2829 }
2830
2831 Transform2D xform = CanvasItemEditor::get_singleton()->get_canvas_transform() * tile_map->get_global_transform_with_canvas();
2832 Vector2 mpos = xform.affine_inverse().xform(CanvasItemEditor::get_singleton()->get_viewport_control()->get_local_mouse_position());
2833
2834 EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
2835 switch (drag_type) {
2836 case DRAG_TYPE_PICK: {
2837 Vector2i coords = tile_map->local_to_map(mpos);
2838 TileMapCell cell = tile_map->get_cell(tile_map_layer, coords);
2839 TileData *tile_data = nullptr;
2840
2841 Ref<TileSetSource> source = tile_set->get_source(cell.source_id);
2842 Ref<TileSetAtlasSource> atlas_source = source;
2843 if (atlas_source.is_valid()) {
2844 tile_data = atlas_source->get_tile_data(cell.get_atlas_coords(), cell.alternative_tile);
2845 }
2846
2847 if (tile_data) {
2848 TileSet::TerrainsPattern terrains_pattern = tile_data->get_terrains_pattern();
2849
2850 // Find the tree item for the right terrain set.
2851 bool need_tree_item_switch = true;
2852 TreeItem *tree_item = terrains_tree->get_selected();
2853 int new_terrain_set = -1;
2854 if (tree_item) {
2855 Dictionary metadata_dict = tree_item->get_metadata(0);
2856 if (metadata_dict.has("terrain_set") && metadata_dict.has("terrain_id")) {
2857 int terrain_set = metadata_dict["terrain_set"];
2858 int terrain_id = metadata_dict["terrain_id"];
2859 if (per_terrain_terrains_patterns[terrain_set][terrain_id].has(terrains_pattern)) {
2860 new_terrain_set = terrain_set;
2861 need_tree_item_switch = false;
2862 }
2863 }
2864 }
2865
2866 if (need_tree_item_switch) {
2867 for (tree_item = terrains_tree->get_root()->get_first_child(); tree_item; tree_item = tree_item->get_next_visible()) {
2868 Dictionary metadata_dict = tree_item->get_metadata(0);
2869 if (metadata_dict.has("terrain_set") && metadata_dict.has("terrain_id")) {
2870 int terrain_set = metadata_dict["terrain_set"];
2871 int terrain_id = metadata_dict["terrain_id"];
2872 if (per_terrain_terrains_patterns[terrain_set][terrain_id].has(terrains_pattern)) {
2873 // Found
2874 new_terrain_set = terrain_set;
2875 tree_item->select(0);
2876 _update_tiles_list();
2877 break;
2878 }
2879 }
2880 }
2881 }
2882
2883 // Find the list item for the given tile.
2884 if (tree_item) {
2885 for (int i = 0; i < terrains_tile_list->get_item_count(); i++) {
2886 Dictionary metadata_dict = terrains_tile_list->get_item_metadata(i);
2887 if (int(metadata_dict["type"]) == SELECTED_TYPE_PATTERN) {
2888 TileSet::TerrainsPattern in_meta_terrains_pattern(*tile_set, new_terrain_set);
2889 in_meta_terrains_pattern.from_array(metadata_dict["terrains_pattern"]);
2890 if (in_meta_terrains_pattern == terrains_pattern) {
2891 terrains_tile_list->select(i);
2892 break;
2893 }
2894 }
2895 }
2896 } else {
2897 ERR_PRINT("Terrain tile not found.");
2898 }
2899 }
2900 picker_button->set_pressed(false);
2901 } break;
2902 case DRAG_TYPE_PAINT: {
2903 undo_redo->create_action(TTR("Paint terrain"));
2904 for (const KeyValue<Vector2i, TileMapCell> &E : drag_modified) {
2905 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));
2906 undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, E.key, E.value.source_id, E.value.get_atlas_coords(), E.value.alternative_tile);
2907 }
2908 undo_redo->commit_action(false);
2909 } break;
2910 case DRAG_TYPE_LINE: {
2911 HashMap<Vector2i, TileMapCell> to_draw = _draw_line(tile_map->local_to_map(drag_start_mouse_pos), tile_map->local_to_map(mpos), drag_erasing);
2912 undo_redo->create_action(TTR("Paint terrain"));
2913 for (const KeyValue<Vector2i, TileMapCell> &E : to_draw) {
2914 if (!drag_erasing && E.value.source_id == TileSet::INVALID_SOURCE) {
2915 continue;
2916 }
2917 undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, E.key, E.value.source_id, E.value.get_atlas_coords(), E.value.alternative_tile);
2918 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));
2919 }
2920 undo_redo->commit_action();
2921 } break;
2922 case DRAG_TYPE_RECT: {
2923 HashMap<Vector2i, TileMapCell> to_draw = _draw_rect(tile_map->local_to_map(drag_start_mouse_pos), tile_map->local_to_map(mpos), drag_erasing);
2924 undo_redo->create_action(TTR("Paint terrain"));
2925 for (const KeyValue<Vector2i, TileMapCell> &E : to_draw) {
2926 if (!drag_erasing && E.value.source_id == TileSet::INVALID_SOURCE) {
2927 continue;
2928 }
2929 undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, E.key, E.value.source_id, E.value.get_atlas_coords(), E.value.alternative_tile);
2930 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));
2931 }
2932 undo_redo->commit_action();
2933 } break;
2934 case DRAG_TYPE_BUCKET: {
2935 undo_redo->create_action(TTR("Paint terrain"));
2936 for (const KeyValue<Vector2i, TileMapCell> &E : drag_modified) {
2937 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));
2938 undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, E.key, E.value.source_id, E.value.get_atlas_coords(), E.value.alternative_tile);
2939 }
2940 undo_redo->commit_action(false);
2941 } break;
2942
2943 default:
2944 break;
2945 }
2946 drag_type = DRAG_TYPE_NONE;
2947}
2948
2949void TileMapEditorTerrainsPlugin::_mouse_exited_viewport() {
2950 has_mouse = false;
2951 CanvasItemEditor::get_singleton()->update_viewport();
2952}
2953
2954void TileMapEditorTerrainsPlugin::_update_selection() {
2955 TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
2956 if (!tile_map) {
2957 return;
2958 }
2959
2960 Ref<TileSet> tile_set = tile_map->get_tileset();
2961 if (!tile_set.is_valid()) {
2962 return;
2963 }
2964
2965 // Get the selected terrain.
2966 selected_terrain_set = -1;
2967 selected_terrains_pattern = TileSet::TerrainsPattern();
2968
2969 TreeItem *selected_tree_item = terrains_tree->get_selected();
2970 if (selected_tree_item && selected_tree_item->get_metadata(0)) {
2971 Dictionary metadata_dict = selected_tree_item->get_metadata(0);
2972 // Selected terrain
2973 selected_terrain_set = metadata_dict["terrain_set"];
2974 selected_terrain = metadata_dict["terrain_id"];
2975
2976 // Selected mode/terrain pattern
2977 if (erase_button->is_pressed()) {
2978 selected_type = SELECTED_TYPE_PATTERN;
2979 selected_terrains_pattern = TileSet::TerrainsPattern(*tile_set, selected_terrain_set);
2980 } else if (terrains_tile_list->is_anything_selected()) {
2981 metadata_dict = terrains_tile_list->get_item_metadata(terrains_tile_list->get_selected_items()[0]);
2982 if (int(metadata_dict["type"]) == SELECTED_TYPE_CONNECT) {
2983 selected_type = SELECTED_TYPE_CONNECT;
2984 } else if (int(metadata_dict["type"]) == SELECTED_TYPE_PATH) {
2985 selected_type = SELECTED_TYPE_PATH;
2986 } else if (int(metadata_dict["type"]) == SELECTED_TYPE_PATTERN) {
2987 selected_type = SELECTED_TYPE_PATTERN;
2988 selected_terrains_pattern = TileSet::TerrainsPattern(*tile_set, selected_terrain_set);
2989 selected_terrains_pattern.from_array(metadata_dict["terrains_pattern"]);
2990 } else {
2991 ERR_FAIL();
2992 }
2993 }
2994 }
2995}
2996
2997bool TileMapEditorTerrainsPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p_event) {
2998 if (!main_vbox_container->is_visible_in_tree()) {
2999 // If the bottom editor is not visible, we ignore inputs.
3000 return false;
3001 }
3002
3003 if (CanvasItemEditor::get_singleton()->get_current_tool() != CanvasItemEditor::TOOL_SELECT) {
3004 return false;
3005 }
3006
3007 TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
3008 if (!tile_map) {
3009 return false;
3010 }
3011
3012 Ref<TileSet> tile_set = tile_map->get_tileset();
3013 if (!tile_set.is_valid()) {
3014 return false;
3015 }
3016
3017 if (tile_map_layer < 0) {
3018 return false;
3019 }
3020 ERR_FAIL_COND_V(tile_map_layer >= tile_map->get_layers_count(), false);
3021
3022 _update_selection();
3023
3024 Ref<InputEventKey> k = p_event;
3025 if (k.is_valid() && k->is_pressed() && !k->is_echo()) {
3026 for (BaseButton *b : viewport_shortcut_buttons) {
3027 if (b->get_shortcut().is_valid() && b->get_shortcut()->matches_event(p_event)) {
3028 b->set_pressed(b->get_button_group().is_valid() || !b->is_pressed());
3029 return true;
3030 }
3031 }
3032 }
3033
3034 Ref<InputEventMouseMotion> mm = p_event;
3035 if (mm.is_valid()) {
3036 has_mouse = true;
3037 Transform2D xform = CanvasItemEditor::get_singleton()->get_canvas_transform() * tile_map->get_global_transform_with_canvas();
3038 Vector2 mpos = xform.affine_inverse().xform(mm->get_position());
3039
3040 switch (drag_type) {
3041 case DRAG_TYPE_PAINT: {
3042 if (selected_terrain_set >= 0) {
3043 HashMap<Vector2i, TileMapCell> to_draw = _draw_line(tile_map->local_to_map(drag_last_mouse_pos), tile_map->local_to_map(mpos), drag_erasing);
3044 for (const KeyValue<Vector2i, TileMapCell> &E : to_draw) {
3045 if (!drag_modified.has(E.key)) {
3046 drag_modified[E.key] = tile_map->get_cell(tile_map_layer, E.key);
3047 }
3048 tile_map->set_cell(tile_map_layer, E.key, E.value.source_id, E.value.get_atlas_coords(), E.value.alternative_tile);
3049 }
3050 }
3051 } break;
3052 default:
3053 break;
3054 }
3055 drag_last_mouse_pos = mpos;
3056 CanvasItemEditor::get_singleton()->update_viewport();
3057
3058 return true;
3059 }
3060
3061 Ref<InputEventMouseButton> mb = p_event;
3062 if (mb.is_valid()) {
3063 has_mouse = true;
3064 Transform2D xform = CanvasItemEditor::get_singleton()->get_canvas_transform() * tile_map->get_global_transform_with_canvas();
3065 Vector2 mpos = xform.affine_inverse().xform(mb->get_position());
3066
3067 if (mb->get_button_index() == MouseButton::LEFT || mb->get_button_index() == MouseButton::RIGHT) {
3068 if (mb->is_pressed()) {
3069 // Pressed
3070 if (erase_button->is_pressed() || mb->get_button_index() == MouseButton::RIGHT) {
3071 drag_erasing = true;
3072 }
3073
3074 if (picker_button->is_pressed()) {
3075 drag_type = DRAG_TYPE_PICK;
3076 } else {
3077 // Paint otherwise.
3078 if (tool_buttons_group->get_pressed_button() == paint_tool_button && !Input::get_singleton()->is_key_pressed(Key::CMD_OR_CTRL) && !Input::get_singleton()->is_key_pressed(Key::SHIFT)) {
3079 if (selected_terrain_set < 0 || selected_terrain < 0 || (selected_type == SELECTED_TYPE_PATTERN && !selected_terrains_pattern.is_valid())) {
3080 return true;
3081 }
3082
3083 drag_type = DRAG_TYPE_PAINT;
3084 drag_start_mouse_pos = mpos;
3085
3086 drag_modified.clear();
3087 Vector2i cell = tile_map->local_to_map(mpos);
3088 HashMap<Vector2i, TileMapCell> to_draw = _draw_line(cell, cell, drag_erasing);
3089 for (const KeyValue<Vector2i, TileMapCell> &E : to_draw) {
3090 drag_modified[E.key] = tile_map->get_cell(tile_map_layer, E.key);
3091 tile_map->set_cell(tile_map_layer, E.key, E.value.source_id, E.value.get_atlas_coords(), E.value.alternative_tile);
3092 }
3093 } else if (tool_buttons_group->get_pressed_button() == line_tool_button || (tool_buttons_group->get_pressed_button() == paint_tool_button && Input::get_singleton()->is_key_pressed(Key::SHIFT) && !Input::get_singleton()->is_key_pressed(Key::CMD_OR_CTRL))) {
3094 if (selected_terrain_set < 0 || selected_terrain < 0 || (selected_type == SELECTED_TYPE_PATTERN && !selected_terrains_pattern.is_valid())) {
3095 return true;
3096 }
3097 drag_type = DRAG_TYPE_LINE;
3098 drag_start_mouse_pos = mpos;
3099 drag_modified.clear();
3100 } else if (tool_buttons_group->get_pressed_button() == rect_tool_button || (tool_buttons_group->get_pressed_button() == paint_tool_button && Input::get_singleton()->is_key_pressed(Key::SHIFT) && Input::get_singleton()->is_key_pressed(Key::CMD_OR_CTRL))) {
3101 if (selected_terrain_set < 0 || selected_terrain < 0 || (selected_type == SELECTED_TYPE_PATTERN && !selected_terrains_pattern.is_valid())) {
3102 return true;
3103 }
3104 drag_type = DRAG_TYPE_RECT;
3105 drag_start_mouse_pos = mpos;
3106 drag_modified.clear();
3107 } else if (tool_buttons_group->get_pressed_button() == bucket_tool_button) {
3108 if (selected_terrain_set < 0 || selected_terrain < 0 || (selected_type == SELECTED_TYPE_PATTERN && !selected_terrains_pattern.is_valid())) {
3109 return true;
3110 }
3111 drag_type = DRAG_TYPE_BUCKET;
3112 drag_start_mouse_pos = mpos;
3113 drag_modified.clear();
3114 Vector<Vector2i> line = TileMapEditor::get_line(tile_map, tile_map->local_to_map(drag_last_mouse_pos), tile_map->local_to_map(mpos));
3115 for (int i = 0; i < line.size(); i++) {
3116 if (!drag_modified.has(line[i])) {
3117 HashMap<Vector2i, TileMapCell> to_draw = _draw_bucket_fill(line[i], bucket_contiguous_checkbox->is_pressed(), drag_erasing);
3118 for (const KeyValue<Vector2i, TileMapCell> &E : to_draw) {
3119 if (!drag_erasing && E.value.source_id == TileSet::INVALID_SOURCE) {
3120 continue;
3121 }
3122 Vector2i coords = E.key;
3123 if (!drag_modified.has(coords)) {
3124 drag_modified.insert(coords, tile_map->get_cell(tile_map_layer, coords));
3125 }
3126 tile_map->set_cell(tile_map_layer, coords, E.value.source_id, E.value.get_atlas_coords(), E.value.alternative_tile);
3127 }
3128 }
3129 }
3130 }
3131 }
3132 } else {
3133 // Released
3134 _stop_dragging();
3135 drag_erasing = false;
3136 }
3137
3138 CanvasItemEditor::get_singleton()->update_viewport();
3139
3140 return true;
3141 }
3142 drag_last_mouse_pos = mpos;
3143 }
3144
3145 return false;
3146}
3147
3148void TileMapEditorTerrainsPlugin::forward_canvas_draw_over_viewport(Control *p_overlay) {
3149 TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
3150 if (!tile_map) {
3151 return;
3152 }
3153
3154 if (tile_map_layer < 0) {
3155 return;
3156 }
3157 ERR_FAIL_INDEX(tile_map_layer, tile_map->get_layers_count());
3158
3159 Ref<TileSet> tile_set = tile_map->get_tileset();
3160 if (!tile_set.is_valid()) {
3161 return;
3162 }
3163
3164 if (!tile_map->is_visible_in_tree()) {
3165 return;
3166 }
3167
3168 Transform2D xform = CanvasItemEditor::get_singleton()->get_canvas_transform() * tile_map->get_global_transform_with_canvas();
3169 Vector2 mpos = tile_map->get_local_mouse_position();
3170 Vector2i tile_shape_size = tile_set->get_tile_size();
3171
3172 // Handle the preview of the tiles to be placed.
3173 if (main_vbox_container->is_visible_in_tree() && has_mouse) { // Only if the tilemap editor is opened and the viewport is hovered.
3174 RBSet<Vector2i> preview;
3175 Rect2i drawn_grid_rect;
3176
3177 if (drag_type == DRAG_TYPE_PICK) {
3178 // Draw the area being picked.
3179 Vector2i coords = tile_map->local_to_map(mpos);
3180 if (tile_map->get_cell_source_id(tile_map_layer, coords) != TileSet::INVALID_SOURCE) {
3181 Transform2D tile_xform;
3182 tile_xform.set_origin(tile_map->map_to_local(coords));
3183 tile_xform.set_scale(tile_shape_size);
3184 tile_set->draw_tile_shape(p_overlay, xform * tile_xform, Color(1.0, 1.0, 1.0), false);
3185 }
3186 } else if (!picker_button->is_pressed() && !(drag_type == DRAG_TYPE_NONE && Input::get_singleton()->is_key_pressed(Key::CMD_OR_CTRL) && !Input::get_singleton()->is_key_pressed(Key::SHIFT))) {
3187 bool expand_grid = false;
3188 if (tool_buttons_group->get_pressed_button() == paint_tool_button && drag_type == DRAG_TYPE_NONE) {
3189 // Preview for a single tile.
3190 preview.insert(tile_map->local_to_map(mpos));
3191 expand_grid = true;
3192 } else if (tool_buttons_group->get_pressed_button() == line_tool_button || drag_type == DRAG_TYPE_LINE) {
3193 if (drag_type == DRAG_TYPE_NONE) {
3194 // Preview for a single tile.
3195 preview.insert(tile_map->local_to_map(mpos));
3196 } else if (drag_type == DRAG_TYPE_LINE) {
3197 // Preview for a line.
3198 Vector<Vector2i> line = TileMapEditor::get_line(tile_map, tile_map->local_to_map(drag_start_mouse_pos), tile_map->local_to_map(mpos));
3199 for (int i = 0; i < line.size(); i++) {
3200 preview.insert(line[i]);
3201 }
3202 expand_grid = true;
3203 }
3204 } else if (drag_type == DRAG_TYPE_RECT) {
3205 // Preview for a rect.
3206 Rect2i rect;
3207 rect.set_position(tile_map->local_to_map(drag_start_mouse_pos));
3208 rect.set_end(tile_map->local_to_map(mpos));
3209 rect = rect.abs();
3210
3211 HashMap<Vector2i, TileSet::TerrainsPattern> to_draw;
3212 for (int x = rect.position.x; x <= rect.get_end().x; x++) {
3213 for (int y = rect.position.y; y <= rect.get_end().y; y++) {
3214 preview.insert(Vector2i(x, y));
3215 }
3216 }
3217 expand_grid = true;
3218 } else if (tool_buttons_group->get_pressed_button() == bucket_tool_button && drag_type == DRAG_TYPE_NONE) {
3219 // Preview for a fill.
3220 preview = _get_cells_for_bucket_fill(tile_map->local_to_map(mpos), bucket_contiguous_checkbox->is_pressed());
3221 }
3222
3223 // Expand the grid if needed
3224 if (expand_grid && !preview.is_empty()) {
3225 drawn_grid_rect = Rect2i(preview.front()->get(), Vector2i(1, 1));
3226 for (const Vector2i &E : preview) {
3227 drawn_grid_rect.expand_to(E);
3228 }
3229 }
3230 }
3231
3232 if (!preview.is_empty()) {
3233 const int fading = 5;
3234
3235 // Draw the lines of the grid behind the preview.
3236 bool display_grid = EDITOR_GET("editors/tiles_editor/display_grid");
3237 if (display_grid) {
3238 Color grid_color = EDITOR_GET("editors/tiles_editor/grid_color");
3239 if (drawn_grid_rect.size.x > 0 && drawn_grid_rect.size.y > 0) {
3240 drawn_grid_rect = drawn_grid_rect.grow(fading);
3241 for (int x = drawn_grid_rect.position.x; x < (drawn_grid_rect.position.x + drawn_grid_rect.size.x); x++) {
3242 for (int y = drawn_grid_rect.position.y; y < (drawn_grid_rect.position.y + drawn_grid_rect.size.y); y++) {
3243 Vector2i pos_in_rect = Vector2i(x, y) - drawn_grid_rect.position;
3244
3245 // Fade out the border of the grid.
3246 float left_opacity = CLAMP(Math::inverse_lerp(0.0f, (float)fading, (float)pos_in_rect.x), 0.0f, 1.0f);
3247 float right_opacity = CLAMP(Math::inverse_lerp((float)drawn_grid_rect.size.x, (float)(drawn_grid_rect.size.x - fading), (float)(pos_in_rect.x + 1)), 0.0f, 1.0f);
3248 float top_opacity = CLAMP(Math::inverse_lerp(0.0f, (float)fading, (float)pos_in_rect.y), 0.0f, 1.0f);
3249 float bottom_opacity = CLAMP(Math::inverse_lerp((float)drawn_grid_rect.size.y, (float)(drawn_grid_rect.size.y - fading), (float)(pos_in_rect.y + 1)), 0.0f, 1.0f);
3250 float opacity = CLAMP(MIN(left_opacity, MIN(right_opacity, MIN(top_opacity, bottom_opacity))) + 0.1, 0.0f, 1.0f);
3251
3252 Transform2D tile_xform;
3253 tile_xform.set_origin(tile_map->map_to_local(Vector2(x, y)));
3254 tile_xform.set_scale(tile_shape_size);
3255 Color color = grid_color;
3256 color.a = color.a * opacity;
3257 tile_set->draw_tile_shape(p_overlay, xform * tile_xform, color, false);
3258 }
3259 }
3260 }
3261 }
3262
3263 // Draw the preview.
3264 for (const Vector2i &E : preview) {
3265 Transform2D tile_xform;
3266 tile_xform.set_origin(tile_map->map_to_local(E));
3267 tile_xform.set_scale(tile_set->get_tile_size());
3268 if (drag_erasing || erase_button->is_pressed()) {
3269 tile_set->draw_tile_shape(p_overlay, xform * tile_xform, Color(0.0, 0.0, 0.0, 0.5), true);
3270 } else {
3271 tile_set->draw_tile_shape(p_overlay, xform * tile_xform, Color(1.0, 1.0, 1.0, 0.5), true);
3272 }
3273 }
3274 }
3275 }
3276}
3277
3278void TileMapEditorTerrainsPlugin::_update_terrains_cache() {
3279 TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
3280 if (!tile_map) {
3281 return;
3282 }
3283
3284 Ref<TileSet> tile_set = tile_map->get_tileset();
3285 if (!tile_set.is_valid()) {
3286 return;
3287 }
3288
3289 // Organizes tiles into structures.
3290 per_terrain_terrains_patterns.resize(tile_set->get_terrain_sets_count());
3291 for (int i = 0; i < tile_set->get_terrain_sets_count(); i++) {
3292 per_terrain_terrains_patterns[i].resize(tile_set->get_terrains_count(i));
3293 for (RBSet<TileSet::TerrainsPattern> &pattern : per_terrain_terrains_patterns[i]) {
3294 pattern.clear();
3295 }
3296 }
3297
3298 for (int source_index = 0; source_index < tile_set->get_source_count(); source_index++) {
3299 int source_id = tile_set->get_source_id(source_index);
3300 Ref<TileSetSource> source = tile_set->get_source(source_id);
3301
3302 Ref<TileSetAtlasSource> atlas_source = source;
3303 if (atlas_source.is_valid()) {
3304 for (int tile_index = 0; tile_index < source->get_tiles_count(); tile_index++) {
3305 Vector2i tile_id = source->get_tile_id(tile_index);
3306 for (int alternative_index = 0; alternative_index < source->get_alternative_tiles_count(tile_id); alternative_index++) {
3307 int alternative_id = source->get_alternative_tile_id(tile_id, alternative_index);
3308
3309 TileData *tile_data = atlas_source->get_tile_data(tile_id, alternative_id);
3310 int terrain_set = tile_data->get_terrain_set();
3311 if (terrain_set >= 0) {
3312 ERR_FAIL_INDEX(terrain_set, (int)per_terrain_terrains_patterns.size());
3313
3314 TileMapCell cell;
3315 cell.source_id = source_id;
3316 cell.set_atlas_coords(tile_id);
3317 cell.alternative_tile = alternative_id;
3318
3319 TileSet::TerrainsPattern terrains_pattern = tile_data->get_terrains_pattern();
3320
3321 // Terrain center bit
3322 int terrain = terrains_pattern.get_terrain();
3323 if (terrain >= 0 && terrain < (int)per_terrain_terrains_patterns[terrain_set].size()) {
3324 per_terrain_terrains_patterns[terrain_set][terrain].insert(terrains_pattern);
3325 }
3326
3327 // Terrain bits.
3328 for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
3329 TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
3330 if (tile_set->is_valid_terrain_peering_bit(terrain_set, bit)) {
3331 terrain = terrains_pattern.get_terrain_peering_bit(bit);
3332 if (terrain >= 0 && terrain < (int)per_terrain_terrains_patterns[terrain_set].size()) {
3333 per_terrain_terrains_patterns[terrain_set][terrain].insert(terrains_pattern);
3334 }
3335 }
3336 }
3337 }
3338 }
3339 }
3340 }
3341 }
3342}
3343
3344void TileMapEditorTerrainsPlugin::_update_terrains_tree() {
3345 terrains_tree->clear();
3346 terrains_tree->create_item();
3347
3348 TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
3349 if (!tile_map) {
3350 return;
3351 }
3352
3353 Ref<TileSet> tile_set = tile_map->get_tileset();
3354 if (!tile_set.is_valid()) {
3355 return;
3356 }
3357
3358 // Fill in the terrain list.
3359 Vector<Vector<Ref<Texture2D>>> icons = tile_set->generate_terrains_icons(Size2(16, 16) * EDSCALE);
3360 for (int terrain_set_index = 0; terrain_set_index < tile_set->get_terrain_sets_count(); terrain_set_index++) {
3361 // Add an item for the terrain set.
3362 TreeItem *terrain_set_tree_item = terrains_tree->create_item();
3363 String matches;
3364 if (tile_set->get_terrain_set_mode(terrain_set_index) == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES) {
3365 terrain_set_tree_item->set_icon(0, main_vbox_container->get_editor_theme_icon(SNAME("TerrainMatchCornersAndSides")));
3366 matches = String(TTR("Matches Corners and Sides"));
3367 } else if (tile_set->get_terrain_set_mode(terrain_set_index) == TileSet::TERRAIN_MODE_MATCH_CORNERS) {
3368 terrain_set_tree_item->set_icon(0, main_vbox_container->get_editor_theme_icon(SNAME("TerrainMatchCorners")));
3369 matches = String(TTR("Matches Corners Only"));
3370 } else {
3371 terrain_set_tree_item->set_icon(0, main_vbox_container->get_editor_theme_icon(SNAME("TerrainMatchSides")));
3372 matches = String(TTR("Matches Sides Only"));
3373 }
3374 terrain_set_tree_item->set_text(0, vformat(TTR("Terrain Set %d (%s)"), terrain_set_index, matches));
3375 terrain_set_tree_item->set_selectable(0, false);
3376
3377 for (int terrain_index = 0; terrain_index < tile_set->get_terrains_count(terrain_set_index); terrain_index++) {
3378 // Add the item to the terrain list.
3379 TreeItem *terrain_tree_item = terrains_tree->create_item(terrain_set_tree_item);
3380 terrain_tree_item->set_text(0, tile_set->get_terrain_name(terrain_set_index, terrain_index));
3381 terrain_tree_item->set_icon_max_width(0, 32 * EDSCALE);
3382 terrain_tree_item->set_icon(0, icons[terrain_set_index][terrain_index]);
3383
3384 Dictionary metadata_dict;
3385 metadata_dict["terrain_set"] = terrain_set_index;
3386 metadata_dict["terrain_id"] = terrain_index;
3387 terrain_tree_item->set_metadata(0, metadata_dict);
3388 }
3389 }
3390}
3391
3392void TileMapEditorTerrainsPlugin::_update_tiles_list() {
3393 terrains_tile_list->clear();
3394
3395 TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
3396 if (!tile_map) {
3397 return;
3398 }
3399
3400 Ref<TileSet> tile_set = tile_map->get_tileset();
3401 if (!tile_set.is_valid()) {
3402 return;
3403 }
3404
3405 TreeItem *selected_tree_item = terrains_tree->get_selected();
3406 if (selected_tree_item && selected_tree_item->get_metadata(0)) {
3407 Dictionary metadata_dict = selected_tree_item->get_metadata(0);
3408 int sel_terrain_set = metadata_dict["terrain_set"];
3409 int sel_terrain_id = metadata_dict["terrain_id"];
3410 ERR_FAIL_INDEX(sel_terrain_set, tile_set->get_terrain_sets_count());
3411 ERR_FAIL_INDEX(sel_terrain_id, tile_set->get_terrains_count(sel_terrain_set));
3412
3413 // Add the two first generic modes
3414 int item_index = terrains_tile_list->add_icon_item(main_vbox_container->get_editor_theme_icon(SNAME("TerrainConnect")));
3415 terrains_tile_list->set_item_tooltip(item_index, TTR("Connect mode: paints a terrain, then connects it with the surrounding tiles with the same terrain."));
3416 Dictionary list_metadata_dict;
3417 list_metadata_dict["type"] = SELECTED_TYPE_CONNECT;
3418 terrains_tile_list->set_item_metadata(item_index, list_metadata_dict);
3419
3420 item_index = terrains_tile_list->add_icon_item(main_vbox_container->get_editor_theme_icon(SNAME("TerrainPath")));
3421 terrains_tile_list->set_item_tooltip(item_index, TTR("Path mode: paints a terrain, thens connects it to the previous tile painted within the same stroke."));
3422 list_metadata_dict = Dictionary();
3423 list_metadata_dict["type"] = SELECTED_TYPE_PATH;
3424 terrains_tile_list->set_item_metadata(item_index, list_metadata_dict);
3425
3426 // Sort the items in a map by the number of corresponding terrains.
3427 RBMap<int, RBSet<TileSet::TerrainsPattern>> sorted;
3428
3429 for (const TileSet::TerrainsPattern &E : per_terrain_terrains_patterns[sel_terrain_set][sel_terrain_id]) {
3430 // Count the number of matching sides/terrains.
3431 int count = 0;
3432
3433 for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
3434 TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
3435 if (tile_set->is_valid_terrain_peering_bit(sel_terrain_set, bit) && E.get_terrain_peering_bit(bit) == sel_terrain_id) {
3436 count++;
3437 }
3438 }
3439 sorted[count].insert(E);
3440 }
3441
3442 for (RBMap<int, RBSet<TileSet::TerrainsPattern>>::Element *E_set = sorted.back(); E_set; E_set = E_set->prev()) {
3443 for (const TileSet::TerrainsPattern &E : E_set->get()) {
3444 TileSet::TerrainsPattern terrains_pattern = E;
3445
3446 // Get the icon.
3447 Ref<Texture2D> icon;
3448 Rect2 region;
3449 bool transpose = false;
3450
3451 double max_probability = -1.0;
3452 for (const TileMapCell &cell : tile_set->get_tiles_for_terrains_pattern(sel_terrain_set, terrains_pattern)) {
3453 Ref<TileSetSource> source = tile_set->get_source(cell.source_id);
3454
3455 Ref<TileSetAtlasSource> atlas_source = source;
3456 if (atlas_source.is_valid()) {
3457 TileData *tile_data = atlas_source->get_tile_data(cell.get_atlas_coords(), cell.alternative_tile);
3458 if (tile_data->get_probability() > max_probability) {
3459 icon = atlas_source->get_texture();
3460 region = atlas_source->get_tile_texture_region(cell.get_atlas_coords());
3461 if (tile_data->get_flip_h()) {
3462 region.position.x += region.size.x;
3463 region.size.x = -region.size.x;
3464 }
3465 if (tile_data->get_flip_v()) {
3466 region.position.y += region.size.y;
3467 region.size.y = -region.size.y;
3468 }
3469 transpose = tile_data->get_transpose();
3470 max_probability = tile_data->get_probability();
3471 }
3472 }
3473 }
3474
3475 // Create the ItemList's item.
3476 item_index = terrains_tile_list->add_item("");
3477 terrains_tile_list->set_item_icon(item_index, icon);
3478 terrains_tile_list->set_item_icon_region(item_index, region);
3479 terrains_tile_list->set_item_icon_transposed(item_index, transpose);
3480 list_metadata_dict = Dictionary();
3481 list_metadata_dict["type"] = SELECTED_TYPE_PATTERN;
3482 list_metadata_dict["terrains_pattern"] = terrains_pattern.as_array();
3483 terrains_tile_list->set_item_metadata(item_index, list_metadata_dict);
3484 }
3485 }
3486 if (terrains_tile_list->get_item_count() > 0) {
3487 terrains_tile_list->select(0);
3488 }
3489 }
3490}
3491
3492void TileMapEditorTerrainsPlugin::_update_theme() {
3493 paint_tool_button->set_icon(main_vbox_container->get_editor_theme_icon(SNAME("Edit")));
3494 line_tool_button->set_icon(main_vbox_container->get_editor_theme_icon(SNAME("Line")));
3495 rect_tool_button->set_icon(main_vbox_container->get_editor_theme_icon(SNAME("Rectangle")));
3496 bucket_tool_button->set_icon(main_vbox_container->get_editor_theme_icon(SNAME("Bucket")));
3497
3498 picker_button->set_icon(main_vbox_container->get_editor_theme_icon(SNAME("ColorPick")));
3499 erase_button->set_icon(main_vbox_container->get_editor_theme_icon(SNAME("Eraser")));
3500
3501 _update_tiles_list();
3502}
3503
3504void TileMapEditorTerrainsPlugin::edit(ObjectID p_tile_map_id, int p_tile_map_layer) {
3505 _stop_dragging(); // Avoids staying in a wrong drag state.
3506
3507 if (tile_map_id != p_tile_map_id) {
3508 tile_map_id = p_tile_map_id;
3509
3510 // Clear the selection.
3511 _update_terrains_cache();
3512 _update_terrains_tree();
3513 _update_tiles_list();
3514 }
3515
3516 tile_map_layer = p_tile_map_layer;
3517}
3518
3519TileMapEditorTerrainsPlugin::TileMapEditorTerrainsPlugin() {
3520 main_vbox_container = memnew(VBoxContainer);
3521 main_vbox_container->connect("tree_entered", callable_mp(this, &TileMapEditorTerrainsPlugin::_update_theme));
3522 main_vbox_container->connect("theme_changed", callable_mp(this, &TileMapEditorTerrainsPlugin::_update_theme));
3523 main_vbox_container->set_name(TTR("Terrains"));
3524
3525 HSplitContainer *tilemap_tab_terrains = memnew(HSplitContainer);
3526 tilemap_tab_terrains->set_h_size_flags(Control::SIZE_EXPAND_FILL);
3527 tilemap_tab_terrains->set_v_size_flags(Control::SIZE_EXPAND_FILL);
3528 main_vbox_container->add_child(tilemap_tab_terrains);
3529
3530 terrains_tree = memnew(Tree);
3531 terrains_tree->set_h_size_flags(Control::SIZE_EXPAND_FILL);
3532 terrains_tree->set_stretch_ratio(0.25);
3533 terrains_tree->set_custom_minimum_size(Size2(70, 0) * EDSCALE);
3534 terrains_tree->set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST);
3535 terrains_tree->set_hide_root(true);
3536 terrains_tree->connect("item_selected", callable_mp(this, &TileMapEditorTerrainsPlugin::_update_tiles_list));
3537 tilemap_tab_terrains->add_child(terrains_tree);
3538
3539 terrains_tile_list = memnew(ItemList);
3540 terrains_tile_list->set_h_size_flags(Control::SIZE_EXPAND_FILL);
3541 terrains_tile_list->set_max_columns(0);
3542 terrains_tile_list->set_same_column_width(true);
3543 terrains_tile_list->set_fixed_icon_size(Size2(32, 32) * EDSCALE);
3544 terrains_tile_list->set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST);
3545 tilemap_tab_terrains->add_child(terrains_tile_list);
3546
3547 // --- Toolbar ---
3548 toolbar = memnew(HBoxContainer);
3549
3550 HBoxContainer *tilemap_tiles_tools_buttons = memnew(HBoxContainer);
3551
3552 tool_buttons_group.instantiate();
3553
3554 paint_tool_button = memnew(Button);
3555 paint_tool_button->set_flat(true);
3556 paint_tool_button->set_toggle_mode(true);
3557 paint_tool_button->set_button_group(tool_buttons_group);
3558 paint_tool_button->set_pressed(true);
3559 paint_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/paint_tool", TTR("Paint"), Key::D));
3560 paint_tool_button->connect("pressed", callable_mp(this, &TileMapEditorTerrainsPlugin::_update_toolbar));
3561 tilemap_tiles_tools_buttons->add_child(paint_tool_button);
3562 viewport_shortcut_buttons.push_back(paint_tool_button);
3563
3564 line_tool_button = memnew(Button);
3565 line_tool_button->set_flat(true);
3566 line_tool_button->set_toggle_mode(true);
3567 line_tool_button->set_button_group(tool_buttons_group);
3568 line_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/line_tool", TTR("Line"), Key::L));
3569 line_tool_button->connect("pressed", callable_mp(this, &TileMapEditorTerrainsPlugin::_update_toolbar));
3570 tilemap_tiles_tools_buttons->add_child(line_tool_button);
3571 viewport_shortcut_buttons.push_back(line_tool_button);
3572
3573 rect_tool_button = memnew(Button);
3574 rect_tool_button->set_flat(true);
3575 rect_tool_button->set_toggle_mode(true);
3576 rect_tool_button->set_button_group(tool_buttons_group);
3577 rect_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/rect_tool", TTR("Rect"), Key::R));
3578 rect_tool_button->connect("pressed", callable_mp(this, &TileMapEditorTerrainsPlugin::_update_toolbar));
3579 tilemap_tiles_tools_buttons->add_child(rect_tool_button);
3580 viewport_shortcut_buttons.push_back(rect_tool_button);
3581
3582 bucket_tool_button = memnew(Button);
3583 bucket_tool_button->set_flat(true);
3584 bucket_tool_button->set_toggle_mode(true);
3585 bucket_tool_button->set_button_group(tool_buttons_group);
3586 bucket_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/bucket_tool", TTR("Bucket"), Key::B));
3587 bucket_tool_button->connect("pressed", callable_mp(this, &TileMapEditorTerrainsPlugin::_update_toolbar));
3588 tilemap_tiles_tools_buttons->add_child(bucket_tool_button);
3589 viewport_shortcut_buttons.push_back(bucket_tool_button);
3590
3591 toolbar->add_child(tilemap_tiles_tools_buttons);
3592
3593 // -- TileMap tool settings --
3594 tools_settings = memnew(HBoxContainer);
3595 toolbar->add_child(tools_settings);
3596
3597 tools_settings_vsep = memnew(VSeparator);
3598 tools_settings->add_child(tools_settings_vsep);
3599
3600 // Picker
3601 picker_button = memnew(Button);
3602 picker_button->set_flat(true);
3603 picker_button->set_toggle_mode(true);
3604 picker_button->set_shortcut(ED_SHORTCUT("tiles_editor/picker", TTR("Picker"), Key::P));
3605 picker_button->connect("pressed", callable_mp(CanvasItemEditor::get_singleton(), &CanvasItemEditor::update_viewport));
3606 tools_settings->add_child(picker_button);
3607 viewport_shortcut_buttons.push_back(picker_button);
3608
3609 // Erase button.
3610 erase_button = memnew(Button);
3611 erase_button->set_flat(true);
3612 erase_button->set_toggle_mode(true);
3613 erase_button->set_shortcut(ED_SHORTCUT("tiles_editor/eraser", TTR("Eraser"), Key::E));
3614 erase_button->connect("pressed", callable_mp(CanvasItemEditor::get_singleton(), &CanvasItemEditor::update_viewport));
3615 tools_settings->add_child(erase_button);
3616 viewport_shortcut_buttons.push_back(erase_button);
3617
3618 // Separator 2.
3619 tools_settings_vsep_2 = memnew(VSeparator);
3620 tools_settings->add_child(tools_settings_vsep_2);
3621
3622 // Continuous checkbox.
3623 bucket_contiguous_checkbox = memnew(CheckBox);
3624 bucket_contiguous_checkbox->set_flat(true);
3625 bucket_contiguous_checkbox->set_text(TTR("Contiguous"));
3626 bucket_contiguous_checkbox->set_pressed(true);
3627 tools_settings->add_child(bucket_contiguous_checkbox);
3628}
3629
3630TileMapEditorTerrainsPlugin::~TileMapEditorTerrainsPlugin() {
3631}
3632
3633void TileMapEditor::_notification(int p_what) {
3634 switch (p_what) {
3635 case NOTIFICATION_THEME_CHANGED: {
3636 missing_tile_texture = get_editor_theme_icon(SNAME("StatusWarning"));
3637 warning_pattern_texture = get_editor_theme_icon(SNAME("WarningPattern"));
3638 advanced_menu_button->set_icon(get_editor_theme_icon(SNAME("Tools")));
3639 toggle_grid_button->set_icon(get_editor_theme_icon(SNAME("Grid")));
3640 toggle_grid_button->set_pressed(EDITOR_GET("editors/tiles_editor/display_grid"));
3641 toggle_highlight_selected_layer_button->set_icon(get_editor_theme_icon(SNAME("TileMapHighlightSelected")));
3642 } break;
3643
3644 case NOTIFICATION_INTERNAL_PROCESS: {
3645 if (is_visible_in_tree() && tileset_changed_needs_update) {
3646 _update_bottom_panel();
3647 _update_layers_selection();
3648 tabs_plugins[tabs_bar->get_current_tab()]->tile_set_changed();
3649 CanvasItemEditor::get_singleton()->update_viewport();
3650 tileset_changed_needs_update = false;
3651 }
3652 } break;
3653
3654 case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
3655 toggle_grid_button->set_pressed(EDITOR_GET("editors/tiles_editor/display_grid"));
3656 } break;
3657
3658 case NOTIFICATION_VISIBILITY_CHANGED: {
3659 TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
3660 if (tile_map) {
3661 if (is_visible_in_tree()) {
3662 tile_map->set_selected_layer(tile_map_layer);
3663 } else {
3664 tile_map->set_selected_layer(-1);
3665 }
3666 }
3667 } break;
3668 }
3669}
3670
3671void TileMapEditor::_on_grid_toggled(bool p_pressed) {
3672 EditorSettings::get_singleton()->set("editors/tiles_editor/display_grid", p_pressed);
3673 CanvasItemEditor::get_singleton()->update_viewport();
3674}
3675
3676void TileMapEditor::_layers_selection_item_selected(int p_index) {
3677 TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
3678 if (!tile_map || tile_map->get_layers_count() <= 0) {
3679 return;
3680 }
3681
3682 tile_map_layer = p_index;
3683 _update_layers_selection();
3684}
3685
3686void TileMapEditor::_advanced_menu_button_id_pressed(int p_id) {
3687 TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
3688 if (!tile_map) {
3689 return;
3690 }
3691
3692 Ref<TileSet> tile_set = tile_map->get_tileset();
3693 if (!tile_set.is_valid()) {
3694 return;
3695 }
3696
3697 if (p_id == 0) { // Replace Tile Proxies
3698 EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
3699 undo_redo->create_action(TTR("Replace Tiles with Proxies"));
3700 for (int layer_index = 0; layer_index < tile_map->get_layers_count(); layer_index++) {
3701 TypedArray<Vector2i> used_cells = tile_map->get_used_cells(layer_index);
3702 for (int i = 0; i < used_cells.size(); i++) {
3703 Vector2i cell_coords = used_cells[i];
3704 TileMapCell from = tile_map->get_cell(layer_index, cell_coords);
3705 Array to_array = tile_set->map_tile_proxy(from.source_id, from.get_atlas_coords(), from.alternative_tile);
3706 TileMapCell to;
3707 to.source_id = to_array[0];
3708 to.set_atlas_coords(to_array[1]);
3709 to.alternative_tile = to_array[2];
3710 if (from != to) {
3711 undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, cell_coords, to.source_id, to.get_atlas_coords(), to.alternative_tile);
3712 undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, cell_coords, from.source_id, from.get_atlas_coords(), from.alternative_tile);
3713 }
3714 }
3715 }
3716 undo_redo->commit_action();
3717 }
3718}
3719
3720void TileMapEditor::_update_bottom_panel() {
3721 TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
3722 if (!tile_map) {
3723 return;
3724 }
3725 Ref<TileSet> tile_set = tile_map->get_tileset();
3726
3727 // Update the visibility of controls.
3728 missing_tileset_label->set_visible(!tile_set.is_valid());
3729 for (TileMapSubEditorPlugin::TabData &tab_data : tabs_data) {
3730 tab_data.panel->hide();
3731 }
3732 if (tile_set.is_valid()) {
3733 tabs_data[tabs_bar->get_current_tab()].panel->show();
3734 }
3735}
3736
3737Vector<Vector2i> TileMapEditor::get_line(TileMap *p_tile_map, Vector2i p_from_cell, Vector2i p_to_cell) {
3738 ERR_FAIL_NULL_V(p_tile_map, Vector<Vector2i>());
3739
3740 Ref<TileSet> tile_set = p_tile_map->get_tileset();
3741 ERR_FAIL_COND_V(!tile_set.is_valid(), Vector<Vector2i>());
3742
3743 if (tile_set->get_tile_shape() == TileSet::TILE_SHAPE_SQUARE) {
3744 return Geometry2D::bresenham_line(p_from_cell, p_to_cell);
3745 } else {
3746 // Adapt the bresenham line algorithm to half-offset shapes.
3747 // See this blog post: http://zvold.blogspot.com/2010/01/bresenhams-line-drawing-algorithm-on_26.html
3748 Vector<Point2i> points;
3749
3750 bool transposed = tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_VERTICAL;
3751 p_from_cell = TileMap::transform_coords_layout(p_from_cell, tile_set->get_tile_offset_axis(), tile_set->get_tile_layout(), TileSet::TILE_LAYOUT_STACKED);
3752 p_to_cell = TileMap::transform_coords_layout(p_to_cell, tile_set->get_tile_offset_axis(), tile_set->get_tile_layout(), TileSet::TILE_LAYOUT_STACKED);
3753 if (transposed) {
3754 SWAP(p_from_cell.x, p_from_cell.y);
3755 SWAP(p_to_cell.x, p_to_cell.y);
3756 }
3757
3758 Vector2i delta = p_to_cell - p_from_cell;
3759 delta = Vector2i(2 * delta.x + ABS(p_to_cell.y % 2) - ABS(p_from_cell.y % 2), delta.y);
3760 Vector2i sign = delta.sign();
3761
3762 Vector2i current = p_from_cell;
3763 points.push_back(TileMap::transform_coords_layout(transposed ? Vector2i(current.y, current.x) : current, tile_set->get_tile_offset_axis(), TileSet::TILE_LAYOUT_STACKED, tile_set->get_tile_layout()));
3764
3765 int err = 0;
3766 if (ABS(delta.y) < ABS(delta.x)) {
3767 Vector2i err_step = 3 * delta.abs();
3768 while (current != p_to_cell) {
3769 err += err_step.y;
3770 if (err > ABS(delta.x)) {
3771 if (sign.x == 0) {
3772 current += Vector2(sign.y, 0);
3773 } else {
3774 current += Vector2(bool(current.y % 2) ^ (sign.x < 0) ? sign.x : 0, sign.y);
3775 }
3776 err -= err_step.x;
3777 } else {
3778 current += Vector2i(sign.x, 0);
3779 err += err_step.y;
3780 }
3781 points.push_back(TileMap::transform_coords_layout(transposed ? Vector2i(current.y, current.x) : current, tile_set->get_tile_offset_axis(), TileSet::TILE_LAYOUT_STACKED, tile_set->get_tile_layout()));
3782 }
3783 } else {
3784 Vector2i err_step = delta.abs();
3785 while (current != p_to_cell) {
3786 err += err_step.x;
3787 if (err > 0) {
3788 if (sign.x == 0) {
3789 current += Vector2(0, sign.y);
3790 } else {
3791 current += Vector2(bool(current.y % 2) ^ (sign.x < 0) ? sign.x : 0, sign.y);
3792 }
3793 err -= err_step.y;
3794 } else {
3795 if (sign.x == 0) {
3796 current += Vector2(0, sign.y);
3797 } else {
3798 current += Vector2(bool(current.y % 2) ^ (sign.x > 0) ? -sign.x : 0, sign.y);
3799 }
3800 err += err_step.y;
3801 }
3802 points.push_back(TileMap::transform_coords_layout(transposed ? Vector2i(current.y, current.x) : current, tile_set->get_tile_offset_axis(), TileSet::TILE_LAYOUT_STACKED, tile_set->get_tile_layout()));
3803 }
3804 }
3805
3806 return points;
3807 }
3808}
3809
3810void TileMapEditor::_tile_map_changed() {
3811 tileset_changed_needs_update = true;
3812}
3813
3814void TileMapEditor::_tab_changed(int p_tab_id) {
3815 // Make the plugin edit the correct tilemap.
3816 tabs_plugins[tabs_bar->get_current_tab()]->edit(tile_map_id, tile_map_layer);
3817
3818 // Update toolbar.
3819 for (TileMapSubEditorPlugin::TabData &tab_data : tabs_data) {
3820 tab_data.toolbar->hide();
3821 }
3822 tabs_data[p_tab_id].toolbar->show();
3823
3824 // Update visible panel.
3825 TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
3826 for (TileMapSubEditorPlugin::TabData &tab_data : tabs_data) {
3827 tab_data.panel->hide();
3828 }
3829 if (tile_map && tile_map->get_tileset().is_valid()) {
3830 tabs_data[tabs_bar->get_current_tab()].panel->show();
3831 }
3832
3833 // Graphical update.
3834 tabs_data[tabs_bar->get_current_tab()].panel->queue_redraw();
3835 CanvasItemEditor::get_singleton()->update_viewport();
3836}
3837
3838void TileMapEditor::_layers_select_next_or_previous(bool p_next) {
3839 TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
3840 if (!tile_map) {
3841 return;
3842 }
3843
3844 if (tile_map->get_layers_count() < 1) {
3845 return;
3846 }
3847
3848 if (tile_map_layer < 0) {
3849 tile_map_layer = 0;
3850 }
3851
3852 int inc = p_next ? 1 : -1;
3853 int origin_layer = tile_map_layer;
3854 tile_map_layer = Math::posmod((tile_map_layer + inc), tile_map->get_layers_count());
3855 while (tile_map_layer != origin_layer) {
3856 if (tile_map->is_layer_enabled(tile_map_layer)) {
3857 break;
3858 }
3859 tile_map_layer = Math::posmod((tile_map_layer + inc), tile_map->get_layers_count());
3860 }
3861
3862 _update_layers_selection();
3863}
3864
3865void TileMapEditor::_update_layers_selection() {
3866 TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
3867 if (!tile_map) {
3868 return;
3869 }
3870
3871 // Update the selected layer.
3872 if (is_visible_in_tree() && tile_map->get_layers_count() >= 1) {
3873 tile_map_layer = CLAMP(tile_map_layer, 0, tile_map->get_layers_count() - 1);
3874
3875 // Search for an enabled layer if the current one is not.
3876 int origin_layer = tile_map_layer;
3877 while (tile_map_layer >= 0 && !tile_map->is_layer_enabled(tile_map_layer)) {
3878 tile_map_layer--;
3879 }
3880 if (tile_map_layer < 0) {
3881 tile_map_layer = origin_layer;
3882 while (tile_map_layer < tile_map->get_layers_count() && !tile_map->is_layer_enabled(tile_map_layer)) {
3883 tile_map_layer++;
3884 }
3885 }
3886 if (tile_map_layer >= tile_map->get_layers_count()) {
3887 tile_map_layer = -1;
3888 }
3889 } else {
3890 tile_map_layer = -1;
3891 }
3892 tile_map->set_selected_layer(toggle_highlight_selected_layer_button->is_pressed() ? tile_map_layer : -1);
3893 tileset_changed_needs_update = false; // Update is not needed here and actually causes problems.
3894
3895 layers_selection_button->clear();
3896 if (tile_map->get_layers_count() > 0) {
3897 // Build the list of layers.
3898 for (int i = 0; i < tile_map->get_layers_count(); i++) {
3899 String name = tile_map->get_layer_name(i);
3900 layers_selection_button->add_item(name.is_empty() ? vformat(TTR("Layer %d"), i) : name, i);
3901 layers_selection_button->set_item_disabled(i, !tile_map->is_layer_enabled(i));
3902 }
3903
3904 layers_selection_button->set_disabled(false);
3905 layers_selection_button->select(tile_map_layer);
3906 } else {
3907 layers_selection_button->set_disabled(true);
3908 layers_selection_button->set_text(TTR("No Layers"));
3909 }
3910
3911 tabs_plugins[tabs_bar->get_current_tab()]->edit(tile_map_id, tile_map_layer);
3912}
3913
3914void TileMapEditor::_move_tile_map_array_element(Object *p_undo_redo, Object *p_edited, String p_array_prefix, int p_from_index, int p_to_pos) {
3915 EditorUndoRedoManager *undo_redo_man = Object::cast_to<EditorUndoRedoManager>(p_undo_redo);
3916 ERR_FAIL_NULL(undo_redo_man);
3917
3918 TileMap *tile_map = Object::cast_to<TileMap>(p_edited);
3919 if (!tile_map) {
3920 return;
3921 }
3922
3923 // Compute the array indices to save.
3924 int begin = 0;
3925 int end;
3926 if (p_array_prefix == "layer_") {
3927 end = tile_map->get_layers_count();
3928 } else {
3929 ERR_FAIL_MSG("Invalid array prefix for TileSet.");
3930 }
3931 if (p_from_index < 0) {
3932 // Adding new.
3933 if (p_to_pos >= 0) {
3934 begin = p_to_pos;
3935 } else {
3936 end = 0; // Nothing to save when adding at the end.
3937 }
3938 } else if (p_to_pos < 0) {
3939 // Removing.
3940 begin = p_from_index;
3941 } else {
3942 // Moving.
3943 begin = MIN(p_from_index, p_to_pos);
3944 end = MIN(MAX(p_from_index, p_to_pos) + 1, end);
3945 }
3946
3947#define ADD_UNDO(obj, property) undo_redo_man->add_undo_property(obj, property, obj->get(property));
3948 // Save layers' properties.
3949 if (p_from_index < 0) {
3950 undo_redo_man->add_undo_method(tile_map, "remove_layer", p_to_pos < 0 ? tile_map->get_layers_count() : p_to_pos);
3951 } else if (p_to_pos < 0) {
3952 undo_redo_man->add_undo_method(tile_map, "add_layer", p_from_index);
3953 }
3954
3955 List<PropertyInfo> properties;
3956 tile_map->get_property_list(&properties);
3957 for (PropertyInfo pi : properties) {
3958 if (pi.name.begins_with(p_array_prefix)) {
3959 String str = pi.name.trim_prefix(p_array_prefix);
3960 int to_char_index = 0;
3961 while (to_char_index < str.length()) {
3962 if (!is_digit(str[to_char_index])) {
3963 break;
3964 }
3965 to_char_index++;
3966 }
3967 if (to_char_index > 0) {
3968 int array_index = str.left(to_char_index).to_int();
3969 if (array_index >= begin && array_index < end) {
3970 ADD_UNDO(tile_map, pi.name);
3971 }
3972 }
3973 }
3974 }
3975#undef ADD_UNDO
3976
3977 if (p_from_index < 0) {
3978 undo_redo_man->add_do_method(tile_map, "add_layer", p_to_pos);
3979 } else if (p_to_pos < 0) {
3980 undo_redo_man->add_do_method(tile_map, "remove_layer", p_from_index);
3981 } else {
3982 undo_redo_man->add_do_method(tile_map, "move_layer", p_from_index, p_to_pos);
3983 }
3984}
3985
3986bool TileMapEditor::forward_canvas_gui_input(const Ref<InputEvent> &p_event) {
3987 if (ED_IS_SHORTCUT("tiles_editor/select_next_layer", p_event) && p_event->is_pressed()) {
3988 _layers_select_next_or_previous(true);
3989 return true;
3990 }
3991
3992 if (ED_IS_SHORTCUT("tiles_editor/select_previous_layer", p_event) && p_event->is_pressed()) {
3993 _layers_select_next_or_previous(false);
3994 return true;
3995 }
3996
3997 return tabs_plugins[tabs_bar->get_current_tab()]->forward_canvas_gui_input(p_event);
3998}
3999
4000void TileMapEditor::forward_canvas_draw_over_viewport(Control *p_overlay) {
4001 TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
4002 if (!tile_map) {
4003 return;
4004 }
4005
4006 Ref<TileSet> tile_set = tile_map->get_tileset();
4007 if (!tile_set.is_valid()) {
4008 return;
4009 }
4010
4011 if (!tile_map->is_visible_in_tree()) {
4012 return;
4013 }
4014
4015 Transform2D xform = CanvasItemEditor::get_singleton()->get_canvas_transform() * tile_map->get_global_transform_with_canvas();
4016 Transform2D xform_inv = xform.affine_inverse();
4017 Vector2i tile_shape_size = tile_set->get_tile_size();
4018
4019 // Draw tiles with invalid IDs in the grid.
4020 if (tile_map_layer >= 0) {
4021 ERR_FAIL_COND(tile_map_layer >= tile_map->get_layers_count());
4022 TypedArray<Vector2i> used_cells = tile_map->get_used_cells(tile_map_layer);
4023 for (int i = 0; i < used_cells.size(); i++) {
4024 Vector2i coords = used_cells[i];
4025 int tile_source_id = tile_map->get_cell_source_id(tile_map_layer, coords);
4026 if (tile_source_id >= 0) {
4027 Vector2i tile_atlas_coords = tile_map->get_cell_atlas_coords(tile_map_layer, coords);
4028 int tile_alternative_tile = tile_map->get_cell_alternative_tile(tile_map_layer, coords);
4029
4030 TileSetSource *source = nullptr;
4031 if (tile_set->has_source(tile_source_id)) {
4032 source = *tile_set->get_source(tile_source_id);
4033 }
4034
4035 if (!source || !source->has_tile(tile_atlas_coords) || !source->has_alternative_tile(tile_atlas_coords, tile_alternative_tile)) {
4036 // Generate a random color from the hashed values of the tiles.
4037 Array a = tile_set->map_tile_proxy(tile_source_id, tile_atlas_coords, tile_alternative_tile);
4038 if (int(a[0]) == tile_source_id && Vector2i(a[1]) == tile_atlas_coords && int(a[2]) == tile_alternative_tile) {
4039 // Only display the pattern if we have no proxy tile.
4040 Array to_hash;
4041 to_hash.push_back(tile_source_id);
4042 to_hash.push_back(tile_atlas_coords);
4043 to_hash.push_back(tile_alternative_tile);
4044 uint32_t hash = RandomPCG(to_hash.hash()).rand();
4045
4046 Color color;
4047 color = color.from_hsv(
4048 (float)((hash >> 24) & 0xFF) / 256.0,
4049 Math::lerp(0.5, 1.0, (float)((hash >> 16) & 0xFF) / 256.0),
4050 Math::lerp(0.5, 1.0, (float)((hash >> 8) & 0xFF) / 256.0),
4051 0.8);
4052
4053 // Draw the scaled tile.
4054 Transform2D tile_xform;
4055 tile_xform.set_origin(tile_map->map_to_local(coords));
4056 tile_xform.set_scale(tile_shape_size);
4057 tile_set->draw_tile_shape(p_overlay, xform * tile_xform, color, true, warning_pattern_texture);
4058 }
4059
4060 // Draw the warning icon.
4061 Vector2::Axis min_axis = missing_tile_texture->get_size().min_axis_index();
4062 Vector2 icon_size;
4063 icon_size[min_axis] = tile_set->get_tile_size()[min_axis] / 3;
4064 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]);
4065 Rect2 rect = Rect2(xform.xform(tile_map->map_to_local(coords)) - (icon_size * xform.get_scale() / 2), icon_size * xform.get_scale());
4066 p_overlay->draw_texture_rect(missing_tile_texture, rect);
4067 }
4068 }
4069 }
4070 }
4071
4072 // Fading on the border.
4073 const int fading = 5;
4074
4075 // Determine the drawn area.
4076 Size2 screen_size = p_overlay->get_size();
4077 Rect2i screen_rect;
4078 screen_rect.position = tile_map->local_to_map(xform_inv.xform(Vector2()));
4079 screen_rect.expand_to(tile_map->local_to_map(xform_inv.xform(Vector2(0, screen_size.height))));
4080 screen_rect.expand_to(tile_map->local_to_map(xform_inv.xform(Vector2(screen_size.width, 0))));
4081 screen_rect.expand_to(tile_map->local_to_map(xform_inv.xform(screen_size)));
4082 screen_rect = screen_rect.grow(1);
4083
4084 Rect2i tilemap_used_rect = tile_map->get_used_rect();
4085
4086 Rect2i displayed_rect = tilemap_used_rect.intersection(screen_rect);
4087 displayed_rect = displayed_rect.grow(fading);
4088
4089 // Reduce the drawn area to avoid crashes if needed.
4090 int max_size = 100;
4091 if (displayed_rect.size.x > max_size) {
4092 displayed_rect = displayed_rect.grow_individual(-(displayed_rect.size.x - max_size) / 2, 0, -(displayed_rect.size.x - max_size) / 2, 0);
4093 }
4094 if (displayed_rect.size.y > max_size) {
4095 displayed_rect = displayed_rect.grow_individual(0, -(displayed_rect.size.y - max_size) / 2, 0, -(displayed_rect.size.y - max_size) / 2);
4096 }
4097
4098 // Draw the grid.
4099 bool display_grid = EDITOR_GET("editors/tiles_editor/display_grid");
4100 if (display_grid) {
4101 Color grid_color = EDITOR_GET("editors/tiles_editor/grid_color");
4102 for (int x = displayed_rect.position.x; x < (displayed_rect.position.x + displayed_rect.size.x); x++) {
4103 for (int y = displayed_rect.position.y; y < (displayed_rect.position.y + displayed_rect.size.y); y++) {
4104 Vector2i pos_in_rect = Vector2i(x, y) - displayed_rect.position;
4105
4106 // Fade out the border of the grid.
4107 float left_opacity = CLAMP(Math::inverse_lerp(0.0f, (float)fading, (float)pos_in_rect.x), 0.0f, 1.0f);
4108 float right_opacity = CLAMP(Math::inverse_lerp((float)displayed_rect.size.x, (float)(displayed_rect.size.x - fading), (float)(pos_in_rect.x + 1)), 0.0f, 1.0f);
4109 float top_opacity = CLAMP(Math::inverse_lerp(0.0f, (float)fading, (float)pos_in_rect.y), 0.0f, 1.0f);
4110 float bottom_opacity = CLAMP(Math::inverse_lerp((float)displayed_rect.size.y, (float)(displayed_rect.size.y - fading), (float)(pos_in_rect.y + 1)), 0.0f, 1.0f);
4111 float opacity = CLAMP(MIN(left_opacity, MIN(right_opacity, MIN(top_opacity, bottom_opacity))) + 0.1, 0.0f, 1.0f);
4112
4113 Transform2D tile_xform;
4114 tile_xform.set_origin(tile_map->map_to_local(Vector2(x, y)));
4115 tile_xform.set_scale(tile_shape_size);
4116 Color color = grid_color;
4117 color.a = color.a * opacity;
4118 tile_set->draw_tile_shape(p_overlay, xform * tile_xform, color, false);
4119 }
4120 }
4121 }
4122
4123 // Draw the IDs for debug.
4124 /*Ref<Font> font = get_theme_font(SNAME("font"), SNAME("Label"));
4125 for (int x = displayed_rect.position.x; x < (displayed_rect.position.x + displayed_rect.size.x); x++) {
4126 for (int y = displayed_rect.position.y; y < (displayed_rect.position.y + displayed_rect.size.y); y++) {
4127 p_overlay->draw_string(font, xform.xform(tile_map->map_to_local(Vector2(x, y))) + Vector2i(-tile_shape_size.x / 2, 0), vformat("%s", Vector2(x, y)));
4128 }
4129 }*/
4130
4131 // Draw the plugins.
4132 tabs_plugins[tabs_bar->get_current_tab()]->forward_canvas_draw_over_viewport(p_overlay);
4133}
4134
4135void TileMapEditor::edit(TileMap *p_tile_map) {
4136 if (p_tile_map && p_tile_map->get_instance_id() == tile_map_id) {
4137 return;
4138 }
4139
4140 TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
4141 if (tile_map) {
4142 // Unselect layer if we are changing tile_map.
4143 if (tile_map != p_tile_map) {
4144 tile_map->set_selected_layer(-1);
4145 }
4146
4147 // Disconnect to changes.
4148 tile_map->disconnect("changed", callable_mp(this, &TileMapEditor::_tile_map_changed));
4149 }
4150
4151 if (p_tile_map) {
4152 // Change the edited object.
4153 tile_map_id = p_tile_map->get_instance_id();
4154 tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
4155 // Connect to changes.
4156 if (!tile_map->is_connected("changed", callable_mp(this, &TileMapEditor::_tile_map_changed))) {
4157 tile_map->connect("changed", callable_mp(this, &TileMapEditor::_tile_map_changed));
4158 }
4159 } else {
4160 tile_map_id = ObjectID();
4161 }
4162
4163 _update_layers_selection();
4164
4165 // Call the plugins.
4166 tabs_plugins[tabs_bar->get_current_tab()]->edit(tile_map_id, tile_map_layer);
4167
4168 _tile_map_changed();
4169}
4170
4171TileMapEditor::TileMapEditor() {
4172 set_process_internal(true);
4173
4174 // Shortcuts.
4175 ED_SHORTCUT("tiles_editor/select_next_layer", TTR("Select Next Tile Map Layer"), Key::PAGEUP);
4176 ED_SHORTCUT("tiles_editor/select_previous_layer", TTR("Select Previous Tile Map Layer"), Key::PAGEDOWN);
4177
4178 // TileMap editor plugins
4179 tile_map_editor_plugins.push_back(memnew(TileMapEditorTilesPlugin));
4180 tile_map_editor_plugins.push_back(memnew(TileMapEditorTerrainsPlugin));
4181
4182 // TabBar.
4183 tabs_bar = memnew(TabBar);
4184 tabs_bar->set_clip_tabs(false);
4185 for (int plugin_index = 0; plugin_index < tile_map_editor_plugins.size(); plugin_index++) {
4186 Vector<TileMapSubEditorPlugin::TabData> tabs_vector = tile_map_editor_plugins[plugin_index]->get_tabs();
4187 for (int tab_index = 0; tab_index < tabs_vector.size(); tab_index++) {
4188 tabs_bar->add_tab(tabs_vector[tab_index].panel->get_name());
4189 tabs_data.push_back(tabs_vector[tab_index]);
4190 tabs_plugins.push_back(tile_map_editor_plugins[plugin_index]);
4191 }
4192 }
4193 tabs_bar->connect("tab_changed", callable_mp(this, &TileMapEditor::_tab_changed));
4194
4195 // --- TileMap toolbar ---
4196 tile_map_toolbar = memnew(HFlowContainer);
4197 tile_map_toolbar->set_h_size_flags(SIZE_EXPAND_FILL);
4198 add_child(tile_map_toolbar);
4199
4200 // Tabs.
4201 tile_map_toolbar->add_child(tabs_bar);
4202
4203 // Tabs toolbars.
4204 for (TileMapSubEditorPlugin::TabData &tab_data : tabs_data) {
4205 tab_data.toolbar->hide();
4206 if (!tab_data.toolbar->get_parent()) {
4207 tile_map_toolbar->add_child(tab_data.toolbar);
4208 }
4209 }
4210
4211 // Wide empty separation control. (like BoxContainer::add_spacer())
4212 Control *c = memnew(Control);
4213 c->set_mouse_filter(MOUSE_FILTER_PASS);
4214 c->set_h_size_flags(SIZE_EXPAND_FILL);
4215 tile_map_toolbar->add_child(c);
4216
4217 // Layer selector.
4218 layers_selection_button = memnew(OptionButton);
4219 layers_selection_button->set_custom_minimum_size(Size2(200, 0));
4220 layers_selection_button->set_text_overrun_behavior(TextServer::OVERRUN_TRIM_ELLIPSIS);
4221 layers_selection_button->set_tooltip_text(TTR("TileMap Layers"));
4222 layers_selection_button->connect("item_selected", callable_mp(this, &TileMapEditor::_layers_selection_item_selected));
4223 tile_map_toolbar->add_child(layers_selection_button);
4224
4225 toggle_highlight_selected_layer_button = memnew(Button);
4226 toggle_highlight_selected_layer_button->set_flat(true);
4227 toggle_highlight_selected_layer_button->set_toggle_mode(true);
4228 toggle_highlight_selected_layer_button->set_pressed(true);
4229 toggle_highlight_selected_layer_button->connect("pressed", callable_mp(this, &TileMapEditor::_update_layers_selection));
4230 toggle_highlight_selected_layer_button->set_tooltip_text(TTR("Highlight Selected TileMap Layer"));
4231 tile_map_toolbar->add_child(toggle_highlight_selected_layer_button);
4232
4233 tile_map_toolbar->add_child(memnew(VSeparator));
4234
4235 // Grid toggle.
4236 toggle_grid_button = memnew(Button);
4237 toggle_grid_button->set_flat(true);
4238 toggle_grid_button->set_toggle_mode(true);
4239 toggle_grid_button->set_tooltip_text(TTR("Toggle grid visibility."));
4240 toggle_grid_button->connect("toggled", callable_mp(this, &TileMapEditor::_on_grid_toggled));
4241 tile_map_toolbar->add_child(toggle_grid_button);
4242
4243 // Advanced settings menu button.
4244 advanced_menu_button = memnew(MenuButton);
4245 advanced_menu_button->set_flat(true);
4246 advanced_menu_button->get_popup()->add_item(TTR("Automatically Replace Tiles with Proxies"));
4247 advanced_menu_button->get_popup()->connect("id_pressed", callable_mp(this, &TileMapEditor::_advanced_menu_button_id_pressed));
4248 tile_map_toolbar->add_child(advanced_menu_button);
4249
4250 missing_tileset_label = memnew(Label);
4251 missing_tileset_label->set_text(TTR("The edited TileMap node has no TileSet resource.\nCreate or load a TileSet resource in the Tile Set property in the inspector."));
4252 missing_tileset_label->set_h_size_flags(SIZE_EXPAND_FILL);
4253 missing_tileset_label->set_v_size_flags(SIZE_EXPAND_FILL);
4254 missing_tileset_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
4255 missing_tileset_label->set_vertical_alignment(VERTICAL_ALIGNMENT_CENTER);
4256 missing_tileset_label->hide();
4257 add_child(missing_tileset_label);
4258
4259 for (unsigned int tab_index = 0; tab_index < tabs_data.size(); tab_index++) {
4260 add_child(tabs_data[tab_index].panel);
4261 tabs_data[tab_index].panel->set_v_size_flags(SIZE_EXPAND_FILL);
4262 tabs_data[tab_index].panel->set_visible(tab_index == 0);
4263 tabs_data[tab_index].panel->set_h_size_flags(SIZE_EXPAND_FILL);
4264 }
4265
4266 _tab_changed(0);
4267
4268 // Registers UndoRedo inspector callback.
4269 EditorNode::get_editor_data().add_move_array_element_function(SNAME("TileMap"), callable_mp(this, &TileMapEditor::_move_tile_map_array_element));
4270}
4271
4272TileMapEditor::~TileMapEditor() {
4273 for (int i = 0; i < tile_map_editor_plugins.size(); i++) {
4274 memdelete(tile_map_editor_plugins[i]);
4275 }
4276}
4277