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 | |
50 | void 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 | |
57 | void TileMapEditorTilesPlugin::_on_random_tile_checkbox_toggled(bool p_pressed) { |
58 | scatter_controls_container->set_visible(p_pressed); |
59 | } |
60 | |
61 | void TileMapEditorTilesPlugin::_on_scattering_spinbox_changed(double p_value) { |
62 | scattering = p_value; |
63 | } |
64 | |
65 | void 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 | |
99 | void 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 | |
124 | Vector<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 | |
131 | void 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 | |
139 | void 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 | |
231 | void 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 | |
278 | void 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 | |
314 | void 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 | |
324 | void 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 | |
356 | void 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 | |
377 | void 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 | |
427 | void 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 | |
435 | void 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 | |
471 | void 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 | |
483 | void 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 | |
504 | bool 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 | |
764 | void 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 | |
1004 | void TileMapEditorTilesPlugin::_mouse_exited_viewport() { |
1005 | has_mouse = false; |
1006 | CanvasItemEditor::get_singleton()->update_viewport(); |
1007 | } |
1008 | |
1009 | TileMapCell 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 | |
1061 | HashMap<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 | |
1110 | HashMap<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 | |
1169 | HashMap<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 | |
1281 | void 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 | |
1504 | void 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 | |
1544 | int 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 | |
1592 | void 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 | |
1662 | void 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 | } |
1680 | void 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 | |
1686 | void 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 | |
1709 | void 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 | |
1783 | void 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 | |
1807 | void 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 | |
1822 | void 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 | |
1894 | void 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 | |
1901 | void 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 | |
2007 | void 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 | |
2052 | void 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 | |
2059 | void 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 | |
2125 | void 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 | |
2135 | TypedArray<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 | |
2143 | void 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 | |
2168 | void 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 | |
2177 | void 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 | |
2183 | TileMapEditorTilesPlugin::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 | |
2489 | TileMapEditorTilesPlugin::~TileMapEditorTilesPlugin() { |
2490 | } |
2491 | |
2492 | void TileMapEditorTerrainsPlugin::tile_set_changed() { |
2493 | _update_terrains_cache(); |
2494 | _update_terrains_tree(); |
2495 | _update_tiles_list(); |
2496 | } |
2497 | |
2498 | void 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 | |
2518 | Vector<TileMapSubEditorPlugin::TabData> TileMapEditorTerrainsPlugin::get_tabs() const { |
2519 | Vector<TileMapSubEditorPlugin::TabData> tabs; |
2520 | tabs.push_back({ toolbar, main_vbox_container }); |
2521 | return tabs; |
2522 | } |
2523 | |
2524 | HashMap<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 | |
2576 | HashMap<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 | |
2623 | HashMap<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 | |
2647 | HashMap<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 | |
2681 | RBSet<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 | |
2792 | HashMap<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 | |
2820 | void 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 | |
2949 | void TileMapEditorTerrainsPlugin::_mouse_exited_viewport() { |
2950 | has_mouse = false; |
2951 | CanvasItemEditor::get_singleton()->update_viewport(); |
2952 | } |
2953 | |
2954 | void 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 | |
2997 | bool 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 | |
3148 | void 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 | |
3278 | void 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 | |
3344 | void 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 | |
3392 | void 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 | |
3492 | void 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 | |
3504 | void 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 | |
3519 | TileMapEditorTerrainsPlugin::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 | |
3630 | TileMapEditorTerrainsPlugin::~TileMapEditorTerrainsPlugin() { |
3631 | } |
3632 | |
3633 | void 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 | |
3671 | void 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 | |
3676 | void 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 | |
3686 | void TileMapEditor::(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 | |
3720 | void 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 | |
3737 | Vector<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 | |
3810 | void TileMapEditor::_tile_map_changed() { |
3811 | tileset_changed_needs_update = true; |
3812 | } |
3813 | |
3814 | void 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 | |
3838 | void 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 | |
3865 | void 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 | |
3914 | void 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 | |
3986 | bool 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 | |
4000 | void 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 | |
4135 | void 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 | |
4171 | TileMapEditor::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 | |
4272 | TileMapEditor::~TileMapEditor() { |
4273 | for (int i = 0; i < tile_map_editor_plugins.size(); i++) { |
4274 | memdelete(tile_map_editor_plugins[i]); |
4275 | } |
4276 | } |
4277 | |