1 /*************************************************************************/
2 /*  tile_map_editor_plugin.cpp                                           */
3 /*************************************************************************/
4 /*                       This file is part of:                           */
5 /*                           GODOT ENGINE                                */
6 /*                      https://godotengine.org                          */
7 /*************************************************************************/
8 /* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur.                 */
9 /* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md)    */
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_plugin.h"
32 
33 #include "os/input.h"
34 #include "os/keyboard.h"
35 
36 #include "canvas_item_editor_plugin.h"
37 #include "editor/editor_scale.h"
38 #include "editor/editor_settings.h"
39 
_notification(int p_what)40 void TileMapEditor::_notification(int p_what) {
41 
42 	switch (p_what) {
43 
44 		case NOTIFICATION_ENTER_TREE: {
45 
46 			transp->set_icon(get_icon("Transpose", "EditorIcons"));
47 			mirror_x->set_icon(get_icon("MirrorX", "EditorIcons"));
48 			mirror_y->set_icon(get_icon("MirrorY", "EditorIcons"));
49 			rotate_0->set_icon(get_icon("Rotate0", "EditorIcons"));
50 			rotate_90->set_icon(get_icon("Rotate90", "EditorIcons"));
51 			rotate_180->set_icon(get_icon("Rotate180", "EditorIcons"));
52 			rotate_270->set_icon(get_icon("Rotate270", "EditorIcons"));
53 
54 		} break;
55 
56 		case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
57 
58 			bool new_show_tile_info = EditorSettings::get_singleton()->get("tile_map/show_tile_info_on_hover");
59 			if (new_show_tile_info != show_tile_info) {
60 				show_tile_info = new_show_tile_info;
61 				tile_info->set_hidden(!show_tile_info);
62 			}
63 
64 			if (is_visible()) {
65 				_update_palette();
66 			}
67 		} break;
68 	}
69 }
70 
_menu_option(int p_option)71 void TileMapEditor::_menu_option(int p_option) {
72 
73 	switch (p_option) {
74 
75 		case OPTION_BUCKET: {
76 
77 			tool = TOOL_BUCKET;
78 
79 			canvas_item_editor->update();
80 		} break;
81 		case OPTION_PICK_TILE: {
82 
83 			tool = TOOL_PICKING;
84 
85 			canvas_item_editor->update();
86 		} break;
87 		case OPTION_SELECT: {
88 
89 			tool = TOOL_SELECTING;
90 			selection_active = false;
91 
92 			canvas_item_editor->update();
93 		} break;
94 		case OPTION_DUPLICATE: {
95 
96 			_update_copydata();
97 
98 			if (selection_active) {
99 				tool = TOOL_DUPLICATING;
100 
101 				canvas_item_editor->update();
102 			}
103 		} break;
104 		case OPTION_ERASE_SELECTION: {
105 
106 			if (!selection_active)
107 				return;
108 
109 			undo_redo->create_action("Erase Selection");
110 			for (int i = rectangle.pos.y; i <= rectangle.pos.y + rectangle.size.y; i++) {
111 				for (int j = rectangle.pos.x; j <= rectangle.pos.x + rectangle.size.x; j++) {
112 
113 					_set_cell(Point2i(j, i), TileMap::INVALID_CELL, false, false, false, true);
114 				}
115 			}
116 			undo_redo->commit_action();
117 
118 			selection_active = false;
119 			copydata.clear();
120 
121 			canvas_item_editor->update();
122 		} break;
123 	}
124 }
125 
_canvas_mouse_enter()126 void TileMapEditor::_canvas_mouse_enter() {
127 
128 	mouse_over = true;
129 	canvas_item_editor->update();
130 }
131 
_canvas_mouse_exit()132 void TileMapEditor::_canvas_mouse_exit() {
133 
134 	mouse_over = false;
135 	canvas_item_editor->update();
136 }
137 
get_selected_tile() const138 int TileMapEditor::get_selected_tile() const {
139 
140 	int item = palette->get_current();
141 
142 	if (item == -1)
143 		return TileMap::INVALID_CELL;
144 
145 	return palette->get_item_metadata(item);
146 }
147 
set_selected_tile(int p_tile)148 void TileMapEditor::set_selected_tile(int p_tile) {
149 
150 	int idx = palette->find_metadata(p_tile);
151 
152 	if (idx >= 0) {
153 		palette->select(idx, true);
154 		palette->ensure_current_is_visible();
155 	}
156 }
157 
_set_cell(const Point2i & p_pos,int p_value,bool p_flip_h,bool p_flip_v,bool p_transpose,bool p_with_undo)158 void TileMapEditor::_set_cell(const Point2i &p_pos, int p_value, bool p_flip_h, bool p_flip_v, bool p_transpose, bool p_with_undo) {
159 
160 	ERR_FAIL_COND(!node);
161 
162 	int prev_val = node->get_cell(p_pos.x, p_pos.y);
163 
164 	bool prev_flip_h = node->is_cell_x_flipped(p_pos.x, p_pos.y);
165 	bool prev_flip_v = node->is_cell_y_flipped(p_pos.x, p_pos.y);
166 	bool prev_transpose = node->is_cell_transposed(p_pos.x, p_pos.y);
167 
168 	if (p_value == prev_val && p_flip_h == prev_flip_h && p_flip_v == prev_flip_v && p_transpose == prev_transpose)
169 		return; //check that it's actually different
170 
171 	if (p_with_undo) {
172 
173 		undo_redo->add_do_method(node, "set_cellv", Point2(p_pos), p_value, p_flip_h, p_flip_v, p_transpose);
174 		undo_redo->add_undo_method(node, "set_cellv", Point2(p_pos), prev_val, prev_flip_h, prev_flip_v, prev_transpose);
175 	} else {
176 
177 		node->set_cell(p_pos.x, p_pos.y, p_value, p_flip_h, p_flip_v, p_transpose);
178 	}
179 }
180 
_text_entered(const String & p_text)181 void TileMapEditor::_text_entered(const String &p_text) {
182 
183 	canvas_item_editor->grab_focus();
184 }
185 
_text_changed(const String & p_text)186 void TileMapEditor::_text_changed(const String &p_text) {
187 
188 	_update_palette();
189 }
190 
_sbox_input(const InputEvent & p_ie)191 void TileMapEditor::_sbox_input(const InputEvent &p_ie) {
192 
193 	if (p_ie.type == InputEvent::KEY && (p_ie.key.scancode == KEY_UP ||
194 												p_ie.key.scancode == KEY_DOWN ||
195 												p_ie.key.scancode == KEY_PAGEUP ||
196 												p_ie.key.scancode == KEY_PAGEDOWN)) {
197 
198 		palette->call("_input_event", p_ie);
199 		search_box->accept_event();
200 	}
201 }
202 
203 // Implementation detail of TileMapEditor::_update_palette();
204 // in modern C++ this could have been inside its body
205 namespace {
206 struct _PaletteEntry {
207 	int id;
208 	String name;
209 
operator <__anon4f54d6320111::_PaletteEntry210 	bool operator<(const _PaletteEntry &p_rhs) const {
211 		return name < p_rhs.name;
212 	}
213 };
214 } // namespace
215 
_update_palette()216 void TileMapEditor::_update_palette() {
217 
218 	if (!node)
219 		return;
220 
221 	int selected = get_selected_tile();
222 	palette->clear();
223 
224 	Ref<TileSet> tileset = node->get_tileset();
225 	if (tileset.is_null())
226 		return;
227 
228 	List<int> tiles;
229 	tileset->get_tile_list(&tiles);
230 
231 	if (tiles.empty())
232 		return;
233 
234 	float min_size = EDITOR_DEF("tile_map/preview_size", 64);
235 	min_size *= EDSCALE;
236 	int hseparation = EDITOR_DEF("tile_map/palette_item_hseparation", 8);
237 	bool show_tile_names = bool(EDITOR_DEF("tile_map/show_tile_names", true));
238 	bool show_tile_ids = bool(EDITOR_DEF("tile_map/show_tile_ids", true));
239 	bool sort_by_name = bool(EDITOR_DEF("tile_map/sort_tiles_by_name", false));
240 
241 	palette->add_constant_override("hseparation", hseparation * EDSCALE);
242 	palette->add_constant_override("vseparation", 8 * EDSCALE);
243 
244 	palette->set_fixed_icon_size(Size2(min_size, min_size));
245 	palette->set_fixed_column_width(min_size * MAX(size_slider->get_val(), 1));
246 
247 	String filter = search_box->get_text().strip_edges();
248 
249 	Vector<_PaletteEntry> entries;
250 
251 	for (List<int>::Element *E = tiles.front(); E; E = E->next()) {
252 
253 		String name = tileset->tile_get_name(E->get());
254 
255 		if (name != "") {
256 			if (show_tile_ids) {
257 				if (sort_by_name) {
258 					name = name + " - " + itos(E->get());
259 				} else {
260 					name = itos(E->get()) + " - " + name;
261 				}
262 			}
263 		} else {
264 			name = "#" + itos(E->get());
265 		}
266 
267 		if (filter != "" && !filter.is_subsequence_ofi(name))
268 			continue;
269 
270 		const _PaletteEntry entry = { E->get(), name };
271 		entries.push_back(entry);
272 	}
273 
274 	if (sort_by_name) {
275 		entries.sort();
276 	}
277 
278 	for (int i = 0; i < entries.size(); i++) {
279 
280 		if (show_tile_names) {
281 			palette->add_item(entries[i].name);
282 		} else {
283 			palette->add_item(String());
284 		}
285 
286 		Ref<Texture> tex = tileset->tile_get_texture(entries[i].id);
287 
288 		if (tex.is_valid()) {
289 			Rect2 region = tileset->tile_get_region(entries[i].id);
290 
291 			if (!region.has_no_area())
292 				palette->set_item_icon_region(palette->get_item_count() - 1, region);
293 
294 			palette->set_item_icon(palette->get_item_count() - 1, tex);
295 		}
296 
297 		palette->set_item_metadata(palette->get_item_count() - 1, entries[i].id);
298 	}
299 
300 	palette->set_same_column_width(true);
301 
302 	if (selected != -1)
303 		set_selected_tile(selected);
304 	else
305 		palette->select(0);
306 }
307 
_pick_tile(const Point2 & p_pos)308 void TileMapEditor::_pick_tile(const Point2 &p_pos) {
309 
310 	int id = node->get_cell(p_pos.x, p_pos.y);
311 
312 	if (id == TileMap::INVALID_CELL)
313 		return;
314 
315 	if (search_box->get_text().strip_edges() != "") {
316 
317 		search_box->set_text("");
318 		_update_palette();
319 	}
320 
321 	set_selected_tile(id);
322 
323 	mirror_x->set_pressed(node->is_cell_x_flipped(p_pos.x, p_pos.y));
324 	mirror_y->set_pressed(node->is_cell_y_flipped(p_pos.x, p_pos.y));
325 	transp->set_pressed(node->is_cell_transposed(p_pos.x, p_pos.y));
326 
327 	_update_transform_buttons();
328 	canvas_item_editor->update();
329 }
330 
_bucket_fill(const Point2i & p_start,bool erase,bool preview)331 DVector<Vector2> TileMapEditor::_bucket_fill(const Point2i &p_start, bool erase, bool preview) {
332 
333 	int prev_id = node->get_cell(p_start.x, p_start.y);
334 	int id = TileMap::INVALID_CELL;
335 	if (!erase) {
336 		id = get_selected_tile();
337 
338 		if (id == TileMap::INVALID_CELL)
339 			return DVector<Vector2>();
340 	} else if (prev_id == TileMap::INVALID_CELL) {
341 		return DVector<Vector2>();
342 	}
343 
344 	if (id == prev_id) {
345 		return DVector<Vector2>();
346 	}
347 
348 	Rect2i r = node->get_item_rect();
349 	r.pos = r.pos / node->get_cell_size();
350 	r.size = r.size / node->get_cell_size();
351 
352 	int area = r.get_area();
353 	if (preview) {
354 		// Test if we can re-use the result from preview bucket fill
355 		bool invalidate_cache = false;
356 		// Area changed
357 		if (r != bucket_cache_rect)
358 			_clear_bucket_cache();
359 		// Cache grid is not initialized
360 		if (bucket_cache_visited == 0) {
361 			bucket_cache_visited = new bool[area];
362 			invalidate_cache = true;
363 		}
364 		// Tile ID changed or position wasn't visited by the previous fill
365 		int loc = (p_start.x - r.get_pos().x) + (p_start.y - r.get_pos().y) * r.get_size().x;
366 		if (prev_id != bucket_cache_tile || !bucket_cache_visited[loc]) {
367 			invalidate_cache = true;
368 		}
369 		if (invalidate_cache) {
370 			for (int i = 0; i < area; ++i)
371 				bucket_cache_visited[i] = false;
372 			bucket_cache = DVector<Vector2>();
373 			bucket_cache_tile = prev_id;
374 			bucket_cache_rect = r;
375 		} else {
376 			return bucket_cache;
377 		}
378 	}
379 
380 	DVector<Vector2> points;
381 
382 	List<Point2i> queue;
383 	queue.push_back(p_start);
384 
385 	while (queue.size()) {
386 
387 		Point2i n = queue.front()->get();
388 		queue.pop_front();
389 
390 		if (!r.has_point(n))
391 			continue;
392 
393 		if (node->get_cell(n.x, n.y) == prev_id) {
394 
395 			if (preview) {
396 				int loc = (n.x - r.get_pos().x) + (n.y - r.get_pos().y) * r.get_size().x;
397 				if (bucket_cache_visited[loc])
398 					continue;
399 				bucket_cache_visited[loc] = true;
400 				bucket_cache.push_back(n);
401 			} else {
402 				node->set_cellv(n, id, flip_h, flip_v, transpose);
403 				points.push_back(n);
404 			}
405 
406 			queue.push_back(n + Point2i(0, 1));
407 			queue.push_back(n + Point2i(0, -1));
408 			queue.push_back(n + Point2i(1, 0));
409 			queue.push_back(n + Point2i(-1, 0));
410 		}
411 	}
412 
413 	return preview ? bucket_cache : points;
414 }
415 
_fill_points(const DVector<Vector2> p_points,const Dictionary & p_op)416 void TileMapEditor::_fill_points(const DVector<Vector2> p_points, const Dictionary &p_op) {
417 
418 	int len = p_points.size();
419 	DVector<Vector2>::Read pr = p_points.read();
420 
421 	int id = p_op["id"];
422 	bool xf = p_op["flip_h"];
423 	bool yf = p_op["flip_v"];
424 	bool tr = p_op["transpose"];
425 
426 	for (int i = 0; i < len; i++) {
427 
428 		_set_cell(pr[i], id, xf, yf, tr);
429 	}
430 }
431 
_erase_points(const DVector<Vector2> p_points)432 void TileMapEditor::_erase_points(const DVector<Vector2> p_points) {
433 
434 	int len = p_points.size();
435 	DVector<Vector2>::Read pr = p_points.read();
436 
437 	for (int i = 0; i < len; i++) {
438 
439 		_set_cell(pr[i], TileMap::INVALID_CELL);
440 	}
441 }
442 
_select(const Point2i & p_from,const Point2i & p_to)443 void TileMapEditor::_select(const Point2i &p_from, const Point2i &p_to) {
444 
445 	Point2i begin = p_from;
446 	Point2i end = p_to;
447 
448 	if (begin.x > end.x) {
449 
450 		SWAP(begin.x, end.x);
451 	}
452 	if (begin.y > end.y) {
453 
454 		SWAP(begin.y, end.y);
455 	}
456 
457 	rectangle.pos = begin;
458 	rectangle.size = end - begin;
459 
460 	canvas_item_editor->update();
461 }
462 
_draw_cell(int p_cell,const Point2i & p_point,bool p_flip_h,bool p_flip_v,bool p_transpose,const Matrix32 & p_xform)463 void TileMapEditor::_draw_cell(int p_cell, const Point2i &p_point, bool p_flip_h, bool p_flip_v, bool p_transpose, const Matrix32 &p_xform) {
464 
465 	Ref<Texture> t = node->get_tileset()->tile_get_texture(p_cell);
466 
467 	if (t.is_null())
468 		return;
469 
470 	Vector2 tile_ofs = node->get_tileset()->tile_get_texture_offset(p_cell);
471 
472 	Rect2 r = node->get_tileset()->tile_get_region(p_cell);
473 	Size2 sc = p_xform.get_scale();
474 
475 	Rect2 rect = Rect2();
476 	rect.pos = node->map_to_world(p_point) + node->get_cell_draw_offset();
477 
478 	if (r.has_no_area()) {
479 		rect.size = t->get_size();
480 	} else {
481 		rect.size = r.size;
482 	}
483 
484 	if (rect.size.y > rect.size.x) {
485 		if ((p_flip_h && (p_flip_v || p_transpose)) || (p_flip_v && !p_transpose))
486 			tile_ofs.y += rect.size.y - rect.size.x;
487 	} else if (rect.size.y < rect.size.x) {
488 		if ((p_flip_v && (p_flip_h || p_transpose)) || (p_flip_h && !p_transpose))
489 			tile_ofs.x += rect.size.x - rect.size.y;
490 	}
491 
492 	if (p_transpose) {
493 		SWAP(tile_ofs.x, tile_ofs.y);
494 	}
495 	if (p_flip_h) {
496 		sc.x *= -1.0;
497 		tile_ofs.x *= -1.0;
498 	}
499 	if (p_flip_v) {
500 		sc.y *= -1.0;
501 		tile_ofs.y *= -1.0;
502 	}
503 
504 	if (node->get_tile_origin() == TileMap::TILE_ORIGIN_TOP_LEFT) {
505 
506 		rect.pos += tile_ofs;
507 	} else if (node->get_tile_origin() == TileMap::TILE_ORIGIN_BOTTOM_LEFT) {
508 		Size2 cell_size = node->get_cell_size();
509 
510 		rect.pos += tile_ofs;
511 
512 		if (p_transpose) {
513 			if (p_flip_h)
514 				rect.pos.x -= cell_size.x;
515 			else
516 				rect.pos.x += cell_size.x;
517 		} else {
518 			if (p_flip_v)
519 				rect.pos.y -= cell_size.y;
520 			else
521 				rect.pos.y += cell_size.y;
522 		}
523 
524 	} else if (node->get_tile_origin() == TileMap::TILE_ORIGIN_CENTER) {
525 		rect.pos += node->get_cell_size() / 2;
526 		Vector2 s = r.size;
527 
528 		Vector2 center = (s / 2) - tile_ofs;
529 
530 		if (p_flip_h)
531 			rect.pos.x -= s.x - center.x;
532 		else
533 			rect.pos.x -= center.x;
534 
535 		if (p_flip_v)
536 			rect.pos.y -= s.y - center.y;
537 		else
538 			rect.pos.y -= center.y;
539 	}
540 
541 	rect.pos = p_xform.xform(rect.pos);
542 	rect.size *= sc;
543 
544 	if (r.has_no_area())
545 		canvas_item_editor->draw_texture_rect(t, rect, false, Color(1, 1, 1, 0.5), p_transpose);
546 	else
547 		canvas_item_editor->draw_texture_rect_region(t, rect, r, Color(1, 1, 1, 0.5), p_transpose);
548 }
549 
_draw_fill_preview(int p_cell,const Point2i & p_point,bool p_flip_h,bool p_flip_v,bool p_transpose,const Matrix32 & p_xform)550 void TileMapEditor::_draw_fill_preview(int p_cell, const Point2i &p_point, bool p_flip_h, bool p_flip_v, bool p_transpose, const Matrix32 &p_xform) {
551 
552 	DVector<Vector2> points = _bucket_fill(p_point, false, true);
553 	DVector<Vector2>::Read pr = points.read();
554 	int len = points.size();
555 	int time_after = OS::get_singleton()->get_ticks_msec();
556 
557 	for (int i = 0; i < len; ++i) {
558 		_draw_cell(p_cell, pr[i], p_flip_h, p_flip_v, p_transpose, p_xform);
559 	}
560 }
561 
_clear_bucket_cache()562 void TileMapEditor::_clear_bucket_cache() {
563 	if (bucket_cache_visited) {
564 		delete[] bucket_cache_visited;
565 		bucket_cache_visited = 0;
566 	}
567 }
568 
_update_copydata()569 void TileMapEditor::_update_copydata() {
570 
571 	copydata.clear();
572 
573 	if (!selection_active)
574 		return;
575 
576 	for (int i = rectangle.pos.y; i <= rectangle.pos.y + rectangle.size.y; i++) {
577 
578 		for (int j = rectangle.pos.x; j <= rectangle.pos.x + rectangle.size.x; j++) {
579 
580 			TileData tcd;
581 
582 			tcd.cell = node->get_cell(j, i);
583 
584 			if (tcd.cell != TileMap::INVALID_CELL) {
585 				tcd.pos = Point2i(j, i);
586 				tcd.flip_h = node->is_cell_x_flipped(j, i);
587 				tcd.flip_v = node->is_cell_y_flipped(j, i);
588 				tcd.transpose = node->is_cell_transposed(j, i);
589 			}
590 
591 			copydata.push_back(tcd);
592 		}
593 	}
594 }
595 
line(int x0,int x1,int y0,int y1)596 static inline Vector<Point2i> line(int x0, int x1, int y0, int y1) {
597 
598 	Vector<Point2i> points;
599 
600 	float dx = ABS(x1 - x0);
601 	float dy = ABS(y1 - y0);
602 
603 	int x = x0;
604 	int y = y0;
605 
606 	int sx = x0 > x1 ? -1 : 1;
607 	int sy = y0 > y1 ? -1 : 1;
608 
609 	if (dx > dy) {
610 		float err = dx / 2;
611 
612 		for (; x != x1; x += sx) {
613 			points.push_back(Vector2(x, y));
614 
615 			err -= dy;
616 			if (err < 0) {
617 				y += sy;
618 				err += dx;
619 			}
620 		}
621 	} else {
622 		float err = dy / 2;
623 
624 		for (; y != y1; y += sy) {
625 			points.push_back(Vector2(x, y));
626 
627 			err -= dx;
628 			if (err < 0) {
629 				x += sx;
630 				err += dy;
631 			}
632 		}
633 	}
634 
635 	points.push_back(Vector2(x, y));
636 
637 	return points;
638 }
639 
forward_input_event(const InputEvent & p_event)640 bool TileMapEditor::forward_input_event(const InputEvent &p_event) {
641 
642 	if (!node || !node->get_tileset().is_valid() || !node->is_visible())
643 		return false;
644 
645 	Matrix32 xform = CanvasItemEditor::get_singleton()->get_canvas_transform() * node->get_global_transform();
646 	Matrix32 xform_inv = xform.affine_inverse();
647 
648 	switch (p_event.type) {
649 
650 		case InputEvent::MOUSE_BUTTON: {
651 
652 			const InputEventMouseButton &mb = p_event.mouse_button;
653 
654 			if (mb.button_index == BUTTON_LEFT) {
655 
656 				if (mb.pressed) {
657 
658 					if (Input::get_singleton()->is_key_pressed(KEY_SPACE))
659 						return false; //drag
660 
661 					if (tool == TOOL_NONE) {
662 
663 						if (mb.mod.shift) {
664 
665 							if (mb.mod.control)
666 								tool = TOOL_RECTANGLE_PAINT;
667 							else
668 								tool = TOOL_LINE_PAINT;
669 
670 							selection_active = false;
671 							rectangle_begin = over_tile;
672 
673 							return true;
674 						}
675 
676 						if (mb.mod.control) {
677 
678 							tool = TOOL_PICKING;
679 							_pick_tile(over_tile);
680 
681 							return true;
682 						}
683 
684 						tool = TOOL_PAINTING;
685 					}
686 
687 					if (tool == TOOL_PAINTING) {
688 
689 						int id = get_selected_tile();
690 
691 						if (id != TileMap::INVALID_CELL) {
692 
693 							tool = TOOL_PAINTING;
694 
695 							paint_undo.clear();
696 							paint_undo[over_tile] = _get_op_from_cell(over_tile);
697 
698 							_set_cell(over_tile, id, flip_h, flip_v, transpose);
699 						}
700 					} else if (tool == TOOL_PICKING) {
701 
702 						_pick_tile(over_tile);
703 					} else if (tool == TOOL_SELECTING) {
704 
705 						selection_active = true;
706 						rectangle_begin = over_tile;
707 					}
708 
709 					return true;
710 
711 				} else {
712 
713 					if (tool != TOOL_NONE) {
714 
715 						if (tool == TOOL_PAINTING) {
716 
717 							int id = get_selected_tile();
718 
719 							if (id != TileMap::INVALID_CELL && paint_undo.size()) {
720 
721 								undo_redo->create_action(TTR("Paint TileMap"));
722 								for (Map<Point2i, CellOp>::Element *E = paint_undo.front(); E; E = E->next()) {
723 
724 									Point2 p = E->key();
725 									undo_redo->add_do_method(node, "set_cellv", p, id, flip_h, flip_v, transpose);
726 									undo_redo->add_undo_method(node, "set_cellv", p, E->get().idx, E->get().xf, E->get().yf, E->get().tr);
727 								}
728 								undo_redo->commit_action();
729 
730 								paint_undo.clear();
731 							}
732 						} else if (tool == TOOL_LINE_PAINT) {
733 
734 							int id = get_selected_tile();
735 
736 							if (id != TileMap::INVALID_CELL) {
737 
738 								undo_redo->create_action("Line Draw");
739 								for (Map<Point2i, CellOp>::Element *E = paint_undo.front(); E; E = E->next()) {
740 
741 									_set_cell(E->key(), id, flip_h, flip_v, transpose, true);
742 								}
743 								undo_redo->commit_action();
744 
745 								paint_undo.clear();
746 
747 								canvas_item_editor->update();
748 							}
749 						} else if (tool == TOOL_RECTANGLE_PAINT) {
750 
751 							int id = get_selected_tile();
752 
753 							if (id != TileMap::INVALID_CELL) {
754 
755 								undo_redo->create_action("Rectangle Paint");
756 								for (int i = rectangle.pos.y; i <= rectangle.pos.y + rectangle.size.y; i++) {
757 									for (int j = rectangle.pos.x; j <= rectangle.pos.x + rectangle.size.x; j++) {
758 
759 										_set_cell(Point2i(j, i), id, flip_h, flip_v, transpose, true);
760 									}
761 								}
762 								undo_redo->commit_action();
763 
764 								canvas_item_editor->update();
765 							}
766 						} else if (tool == TOOL_DUPLICATING) {
767 
768 							Point2 ofs = over_tile - rectangle.pos;
769 
770 							undo_redo->create_action(TTR("Duplicate"));
771 							for (List<TileData>::Element *E = copydata.front(); E; E = E->next()) {
772 
773 								_set_cell(E->get().pos + ofs, E->get().cell, E->get().flip_h, E->get().flip_v, E->get().transpose, true);
774 							}
775 							undo_redo->commit_action();
776 
777 							copydata.clear();
778 
779 							canvas_item_editor->update();
780 
781 						} else if (tool == TOOL_SELECTING) {
782 
783 							canvas_item_editor->update();
784 
785 						} else if (tool == TOOL_BUCKET) {
786 
787 							Dictionary pop;
788 							pop["id"] = node->get_cell(over_tile.x, over_tile.y);
789 							pop["flip_h"] = node->is_cell_x_flipped(over_tile.x, over_tile.y);
790 							pop["flip_v"] = node->is_cell_y_flipped(over_tile.x, over_tile.y);
791 							pop["transpose"] = node->is_cell_transposed(over_tile.x, over_tile.y);
792 
793 							DVector<Vector2> points = _bucket_fill(over_tile);
794 
795 							if (points.size() == 0)
796 								return false;
797 
798 							Dictionary op;
799 							op["id"] = get_selected_tile();
800 							op["flip_h"] = flip_h;
801 							op["flip_v"] = flip_v;
802 							op["transpose"] = transpose;
803 
804 							undo_redo->create_action("Bucket Fill");
805 
806 							undo_redo->add_do_method(this, "_fill_points", points, op);
807 							undo_redo->add_undo_method(this, "_fill_points", points, pop);
808 
809 							undo_redo->commit_action();
810 						}
811 
812 						tool = TOOL_NONE;
813 
814 						return true;
815 					}
816 				}
817 			} else if (mb.button_index == BUTTON_RIGHT) {
818 
819 				if (mb.pressed) {
820 
821 					if (tool == TOOL_SELECTING || selection_active) {
822 
823 						tool = TOOL_NONE;
824 						selection_active = false;
825 
826 						canvas_item_editor->update();
827 
828 						return true;
829 					}
830 
831 					if (tool == TOOL_DUPLICATING) {
832 
833 						tool = TOOL_NONE;
834 						copydata.clear();
835 
836 						canvas_item_editor->update();
837 
838 						return true;
839 					}
840 
841 					if (tool == TOOL_NONE) {
842 
843 						paint_undo.clear();
844 
845 						Point2 local = node->world_to_map(xform_inv.xform(Point2(mb.x, mb.y)));
846 
847 						if (mb.mod.shift) {
848 
849 							if (mb.mod.control)
850 								tool = TOOL_RECTANGLE_ERASE;
851 							else
852 								tool = TOOL_LINE_ERASE;
853 
854 							selection_active = false;
855 							rectangle_begin = local;
856 						} else {
857 
858 							tool = TOOL_ERASING;
859 
860 							paint_undo[local] = _get_op_from_cell(local);
861 							_set_cell(local, TileMap::INVALID_CELL);
862 						}
863 
864 						return true;
865 					}
866 
867 				} else {
868 					if (tool == TOOL_ERASING || tool == TOOL_RECTANGLE_ERASE || tool == TOOL_LINE_ERASE) {
869 
870 						if (paint_undo.size()) {
871 							undo_redo->create_action(TTR("Erase TileMap"));
872 							for (Map<Point2i, CellOp>::Element *E = paint_undo.front(); E; E = E->next()) {
873 
874 								Point2 p = E->key();
875 								undo_redo->add_do_method(node, "set_cellv", p, TileMap::INVALID_CELL, false, false, false);
876 								undo_redo->add_undo_method(node, "set_cellv", p, E->get().idx, E->get().xf, E->get().yf, E->get().tr);
877 							}
878 
879 							undo_redo->commit_action();
880 							paint_undo.clear();
881 						}
882 
883 						if (tool == TOOL_RECTANGLE_ERASE || tool == TOOL_LINE_ERASE) {
884 							canvas_item_editor->update();
885 						}
886 
887 						tool = TOOL_NONE;
888 
889 						return true;
890 
891 					} else if (tool == TOOL_BUCKET) {
892 
893 						Dictionary pop;
894 						pop["id"] = node->get_cell(over_tile.x, over_tile.y);
895 						pop["flip_h"] = node->is_cell_x_flipped(over_tile.x, over_tile.y);
896 						pop["flip_v"] = node->is_cell_y_flipped(over_tile.x, over_tile.y);
897 						pop["transpose"] = node->is_cell_transposed(over_tile.x, over_tile.y);
898 
899 						DVector<Vector2> points = _bucket_fill(over_tile, true);
900 
901 						if (points.size() == 0)
902 							return false;
903 
904 						undo_redo->create_action("Bucket Fill");
905 
906 						undo_redo->add_do_method(this, "_erase_points", points);
907 						undo_redo->add_undo_method(this, "_fill_points", points, pop);
908 
909 						undo_redo->commit_action();
910 					}
911 				}
912 			}
913 		} break;
914 		case InputEvent::MOUSE_MOTION: {
915 
916 			const InputEventMouseMotion &mm = p_event.mouse_motion;
917 
918 			Point2i new_over_tile = node->world_to_map(xform_inv.xform(Point2(mm.x, mm.y)));
919 
920 			if (new_over_tile != over_tile) {
921 
922 				over_tile = new_over_tile;
923 				canvas_item_editor->update();
924 			}
925 
926 			if (show_tile_info) {
927 				int tile_under = node->get_cell(over_tile.x, over_tile.y);
928 				String tile_name = "none";
929 
930 				if (node->get_tileset()->has_tile(tile_under))
931 					tile_name = node->get_tileset()->tile_get_name(tile_under);
932 				tile_info->set_text(String::num(over_tile.x) + ", " + String::num(over_tile.y) + " [" + tile_name + "]");
933 				tile_info->show();
934 			}
935 
936 			if (tool == TOOL_PAINTING) {
937 
938 				int id = get_selected_tile();
939 				if (id != TileMap::INVALID_CELL) {
940 
941 					if (!paint_undo.has(over_tile)) {
942 						paint_undo[over_tile] = _get_op_from_cell(over_tile);
943 					}
944 
945 					_set_cell(over_tile, id, flip_h, flip_v, transpose);
946 
947 					return true;
948 				}
949 			}
950 
951 			if (tool == TOOL_SELECTING) {
952 
953 				_select(rectangle_begin, over_tile);
954 
955 				return true;
956 			}
957 
958 			if (tool == TOOL_LINE_PAINT || tool == TOOL_LINE_ERASE) {
959 
960 				int id = get_selected_tile();
961 				bool erasing = (tool == TOOL_LINE_ERASE);
962 
963 				if (erasing && paint_undo.size()) {
964 
965 					for (Map<Point2i, CellOp>::Element *E = paint_undo.front(); E; E = E->next()) {
966 
967 						_set_cell(E->key(), E->get().idx, E->get().xf, E->get().yf, E->get().tr);
968 					}
969 				}
970 
971 				paint_undo.clear();
972 
973 				if (id != TileMap::INVALID_CELL) {
974 
975 					Vector<Point2i> points = line(rectangle_begin.x, over_tile.x, rectangle_begin.y, over_tile.y);
976 
977 					for (int i = 0; i < points.size(); i++) {
978 
979 						paint_undo[points[i]] = _get_op_from_cell(points[i]);
980 
981 						if (erasing)
982 							_set_cell(points[i], TileMap::INVALID_CELL);
983 					}
984 
985 					canvas_item_editor->update();
986 				}
987 
988 				return true;
989 			}
990 			if (tool == TOOL_RECTANGLE_PAINT || tool == TOOL_RECTANGLE_ERASE) {
991 
992 				_select(rectangle_begin, over_tile);
993 
994 				if (tool == TOOL_RECTANGLE_ERASE) {
995 
996 					if (paint_undo.size()) {
997 
998 						for (Map<Point2i, CellOp>::Element *E = paint_undo.front(); E; E = E->next()) {
999 
1000 							_set_cell(E->key(), E->get().idx, E->get().xf, E->get().yf, E->get().tr);
1001 						}
1002 					}
1003 
1004 					paint_undo.clear();
1005 
1006 					for (int i = rectangle.pos.y; i <= rectangle.pos.y + rectangle.size.y; i++) {
1007 						for (int j = rectangle.pos.x; j <= rectangle.pos.x + rectangle.size.x; j++) {
1008 
1009 							Point2i tile = Point2i(j, i);
1010 							paint_undo[tile] = _get_op_from_cell(tile);
1011 
1012 							_set_cell(tile, TileMap::INVALID_CELL);
1013 						}
1014 					}
1015 				}
1016 
1017 				return true;
1018 			}
1019 			if (tool == TOOL_ERASING) {
1020 
1021 				if (!paint_undo.has(over_tile)) {
1022 					paint_undo[over_tile] = _get_op_from_cell(over_tile);
1023 				}
1024 
1025 				_set_cell(over_tile, TileMap::INVALID_CELL);
1026 
1027 				return true;
1028 			}
1029 			if (tool == TOOL_PICKING && Input::get_singleton()->is_mouse_button_pressed(BUTTON_LEFT)) {
1030 
1031 				_pick_tile(over_tile);
1032 
1033 				return true;
1034 			}
1035 		} break;
1036 		case InputEvent::KEY: {
1037 
1038 			const InputEventKey &k = p_event.key;
1039 
1040 			if (!k.pressed)
1041 				break;
1042 
1043 			if (k.scancode == KEY_ESCAPE) {
1044 
1045 				if (tool == TOOL_DUPLICATING)
1046 					copydata.clear();
1047 				else if (tool == TOOL_SELECTING || selection_active)
1048 					selection_active = false;
1049 
1050 				tool = TOOL_NONE;
1051 
1052 				canvas_item_editor->update();
1053 
1054 				return true;
1055 			}
1056 
1057 			if (tool != TOOL_NONE || !mouse_over)
1058 				return false;
1059 
1060 			if (ED_IS_SHORTCUT("tile_map_editor/erase_selection", p_event)) {
1061 				_menu_option(OPTION_ERASE_SELECTION);
1062 
1063 				return true;
1064 			}
1065 			if (ED_IS_SHORTCUT("tile_map_editor/select", p_event)) {
1066 				tool = TOOL_SELECTING;
1067 				selection_active = false;
1068 
1069 				canvas_item_editor->update();
1070 
1071 				return true;
1072 			}
1073 			if (ED_IS_SHORTCUT("tile_map_editor/duplicate_selection", p_event)) {
1074 				_update_copydata();
1075 
1076 				if (selection_active) {
1077 					tool = TOOL_DUPLICATING;
1078 
1079 					canvas_item_editor->update();
1080 
1081 					return true;
1082 				}
1083 			}
1084 			if (ED_IS_SHORTCUT("tile_map_editor/find_tile", p_event)) {
1085 				search_box->select_all();
1086 				search_box->grab_focus();
1087 
1088 				return true;
1089 			}
1090 			if (ED_IS_SHORTCUT("tile_map_editor/mirror_x", p_event)) {
1091 				flip_h = !flip_h;
1092 				mirror_x->set_pressed(flip_h);
1093 				canvas_item_editor->update();
1094 				return true;
1095 			}
1096 			if (ED_IS_SHORTCUT("tile_map_editor/mirror_y", p_event)) {
1097 				flip_v = !flip_v;
1098 				mirror_y->set_pressed(flip_v);
1099 				canvas_item_editor->update();
1100 				return true;
1101 			}
1102 			if (ED_IS_SHORTCUT("tile_map_editor/transpose", p_event)) {
1103 				transpose = !transpose;
1104 				transp->set_pressed(transpose);
1105 				canvas_item_editor->update();
1106 				return true;
1107 			}
1108 		} break;
1109 	}
1110 
1111 	return false;
1112 }
1113 
_canvas_draw()1114 void TileMapEditor::_canvas_draw() {
1115 
1116 	if (!node)
1117 		return;
1118 
1119 	Matrix32 cell_xf = node->get_cell_transform();
1120 
1121 	Matrix32 xform = CanvasItemEditor::get_singleton()->get_canvas_transform() * node->get_global_transform();
1122 	Matrix32 xform_inv = xform.affine_inverse();
1123 
1124 	Size2 screen_size = canvas_item_editor->get_size();
1125 	{
1126 		Rect2 aabb;
1127 		aabb.pos = node->world_to_map(xform_inv.xform(Vector2()));
1128 		aabb.expand_to(node->world_to_map(xform_inv.xform(Vector2(0, screen_size.height))));
1129 		aabb.expand_to(node->world_to_map(xform_inv.xform(Vector2(screen_size.width, 0))));
1130 		aabb.expand_to(node->world_to_map(xform_inv.xform(screen_size)));
1131 		Rect2i si = aabb.grow(1.0);
1132 
1133 		if (node->get_half_offset() != TileMap::HALF_OFFSET_X) {
1134 
1135 			int max_lines = 2000; //avoid crash if size too smal
1136 
1137 			for (int i = (si.pos.x) - 1; i <= (si.pos.x + si.size.x); i++) {
1138 
1139 				Vector2 from = xform.xform(node->map_to_world(Vector2(i, si.pos.y)));
1140 				Vector2 to = xform.xform(node->map_to_world(Vector2(i, si.pos.y + si.size.y + 1)));
1141 
1142 				Color col = i == 0 ? Color(1, 0.8, 0.2, 0.5) : Color(1, 0.3, 0.1, 0.2);
1143 				canvas_item_editor->draw_line(from, to, col, 1);
1144 				if (max_lines-- == 0)
1145 					break;
1146 			}
1147 		} else {
1148 
1149 			int max_lines = 10000; //avoid crash if size too smal
1150 
1151 			for (int i = (si.pos.x) - 1; i <= (si.pos.x + si.size.x); i++) {
1152 
1153 				for (int j = (si.pos.y) - 1; j <= (si.pos.y + si.size.y); j++) {
1154 
1155 					Vector2 ofs;
1156 					if (ABS(j) & 1) {
1157 						ofs = cell_xf[0] * 0.5;
1158 					}
1159 
1160 					Vector2 from = xform.xform(node->map_to_world(Vector2(i, j), true) + ofs);
1161 					Vector2 to = xform.xform(node->map_to_world(Vector2(i, j + 1), true) + ofs);
1162 					Color col = i == 0 ? Color(1, 0.8, 0.2, 0.5) : Color(1, 0.3, 0.1, 0.2);
1163 					canvas_item_editor->draw_line(from, to, col, 1);
1164 
1165 					if (max_lines-- == 0)
1166 						break;
1167 				}
1168 			}
1169 		}
1170 
1171 		int max_lines = 10000; //avoid crash if size too smal
1172 
1173 		if (node->get_half_offset() != TileMap::HALF_OFFSET_Y) {
1174 
1175 			for (int i = (si.pos.y) - 1; i <= (si.pos.y + si.size.y); i++) {
1176 
1177 				Vector2 from = xform.xform(node->map_to_world(Vector2(si.pos.x, i)));
1178 				Vector2 to = xform.xform(node->map_to_world(Vector2(si.pos.x + si.size.x + 1, i)));
1179 
1180 				Color col = i == 0 ? Color(1, 0.8, 0.2, 0.5) : Color(1, 0.3, 0.1, 0.2);
1181 				canvas_item_editor->draw_line(from, to, col, 1);
1182 
1183 				if (max_lines-- == 0)
1184 					break;
1185 			}
1186 		} else {
1187 
1188 			for (int i = (si.pos.y) - 1; i <= (si.pos.y + si.size.y); i++) {
1189 
1190 				for (int j = (si.pos.x) - 1; j <= (si.pos.x + si.size.x); j++) {
1191 
1192 					Vector2 ofs;
1193 					if (ABS(j) & 1) {
1194 						ofs = cell_xf[1] * 0.5;
1195 					}
1196 
1197 					Vector2 from = xform.xform(node->map_to_world(Vector2(j, i), true) + ofs);
1198 					Vector2 to = xform.xform(node->map_to_world(Vector2(j + 1, i), true) + ofs);
1199 					Color col = i == 0 ? Color(1, 0.8, 0.2, 0.5) : Color(1, 0.3, 0.1, 0.2);
1200 					canvas_item_editor->draw_line(from, to, col, 1);
1201 
1202 					if (max_lines-- == 0)
1203 						break;
1204 				}
1205 			}
1206 		}
1207 	}
1208 
1209 	if (selection_active) {
1210 
1211 		Vector<Vector2> points;
1212 		points.push_back(xform.xform(node->map_to_world((rectangle.pos))));
1213 		points.push_back(xform.xform(node->map_to_world((rectangle.pos + Point2(rectangle.size.x + 1, 0)))));
1214 		points.push_back(xform.xform(node->map_to_world((rectangle.pos + Point2(rectangle.size.x + 1, rectangle.size.y + 1)))));
1215 		points.push_back(xform.xform(node->map_to_world((rectangle.pos + Point2(0, rectangle.size.y + 1)))));
1216 
1217 		canvas_item_editor->draw_colored_polygon(points, Color(0.2, 0.8, 1, 0.4));
1218 	}
1219 
1220 	if (mouse_over) {
1221 
1222 		Vector2 endpoints[4] = {
1223 			node->map_to_world(over_tile, true),
1224 			node->map_to_world((over_tile + Point2(1, 0)), true),
1225 			node->map_to_world((over_tile + Point2(1, 1)), true),
1226 			node->map_to_world((over_tile + Point2(0, 1)), true)
1227 		};
1228 
1229 		for (int i = 0; i < 4; i++) {
1230 			if (node->get_half_offset() == TileMap::HALF_OFFSET_X && ABS(over_tile.y) & 1)
1231 				endpoints[i] += cell_xf[0] * 0.5;
1232 			if (node->get_half_offset() == TileMap::HALF_OFFSET_Y && ABS(over_tile.x) & 1)
1233 				endpoints[i] += cell_xf[1] * 0.5;
1234 			endpoints[i] = xform.xform(endpoints[i]);
1235 		}
1236 		Color col;
1237 		if (node->get_cell(over_tile.x, over_tile.y) != TileMap::INVALID_CELL)
1238 			col = Color(0.2, 0.8, 1.0, 0.8);
1239 		else
1240 			col = Color(1.0, 0.4, 0.2, 0.8);
1241 
1242 		for (int i = 0; i < 4; i++)
1243 			canvas_item_editor->draw_line(endpoints[i], endpoints[(i + 1) % 4], col, 2);
1244 
1245 		bool bucket_preview = EditorSettings::get_singleton()->get("tile_map/bucket_fill_preview");
1246 		if (tool == TOOL_SELECTING || tool == TOOL_PICKING || !bucket_preview) {
1247 			return;
1248 		}
1249 
1250 		if (tool == TOOL_LINE_PAINT) {
1251 
1252 			if (paint_undo.empty())
1253 				return;
1254 
1255 			int id = get_selected_tile();
1256 
1257 			if (id == TileMap::INVALID_CELL)
1258 				return;
1259 
1260 			for (Map<Point2i, CellOp>::Element *E = paint_undo.front(); E; E = E->next()) {
1261 
1262 				_draw_cell(id, E->key(), flip_h, flip_v, transpose, xform);
1263 			}
1264 
1265 		} else if (tool == TOOL_RECTANGLE_PAINT) {
1266 
1267 			int id = get_selected_tile();
1268 
1269 			if (id == TileMap::INVALID_CELL)
1270 				return;
1271 
1272 			for (int i = rectangle.pos.y; i <= rectangle.pos.y + rectangle.size.y; i++) {
1273 				for (int j = rectangle.pos.x; j <= rectangle.pos.x + rectangle.size.x; j++) {
1274 
1275 					_draw_cell(id, Point2i(j, i), flip_h, flip_v, transpose, xform);
1276 				}
1277 			}
1278 		} else if (tool == TOOL_DUPLICATING) {
1279 
1280 			if (copydata.empty())
1281 				return;
1282 
1283 			Ref<TileSet> ts = node->get_tileset();
1284 
1285 			if (ts.is_null())
1286 				return;
1287 
1288 			Point2 ofs = over_tile - rectangle.pos;
1289 
1290 			for (List<TileData>::Element *E = copydata.front(); E; E = E->next()) {
1291 
1292 				if (!ts->has_tile(E->get().cell))
1293 					continue;
1294 
1295 				TileData tcd = E->get();
1296 
1297 				_draw_cell(tcd.cell, tcd.pos + ofs, tcd.flip_h, tcd.flip_v, tcd.transpose, xform);
1298 			}
1299 
1300 			Rect2i duplicate = rectangle;
1301 			duplicate.pos = over_tile;
1302 
1303 			Vector<Vector2> points;
1304 			points.push_back(xform.xform(node->map_to_world(duplicate.pos)));
1305 			points.push_back(xform.xform(node->map_to_world((duplicate.pos + Point2(duplicate.size.x + 1, 0)))));
1306 			points.push_back(xform.xform(node->map_to_world((duplicate.pos + Point2(duplicate.size.x + 1, duplicate.size.y + 1)))));
1307 			points.push_back(xform.xform(node->map_to_world((duplicate.pos + Point2(0, duplicate.size.y + 1)))));
1308 
1309 			canvas_item_editor->draw_colored_polygon(points, Color(0.2, 1.0, 0.8, 0.2));
1310 
1311 		} else if (tool == TOOL_BUCKET) {
1312 
1313 			int tile = get_selected_tile();
1314 			_draw_fill_preview(tile, over_tile, flip_h, flip_v, transpose, xform);
1315 
1316 		} else {
1317 
1318 			int st = get_selected_tile();
1319 
1320 			if (st == TileMap::INVALID_CELL)
1321 				return;
1322 
1323 			_draw_cell(st, over_tile, flip_h, flip_v, transpose, xform);
1324 		}
1325 	}
1326 }
1327 
edit(Node * p_tile_map)1328 void TileMapEditor::edit(Node *p_tile_map) {
1329 
1330 	search_box->set_text("");
1331 
1332 	if (!canvas_item_editor) {
1333 		canvas_item_editor = CanvasItemEditor::get_singleton()->get_viewport_control();
1334 	}
1335 
1336 	if (node)
1337 		node->disconnect("settings_changed", this, "_tileset_settings_changed");
1338 	if (p_tile_map) {
1339 
1340 		node = p_tile_map->cast_to<TileMap>();
1341 		if (!canvas_item_editor->is_connected("draw", this, "_canvas_draw"))
1342 			canvas_item_editor->connect("draw", this, "_canvas_draw");
1343 		if (!canvas_item_editor->is_connected("mouse_enter", this, "_canvas_mouse_enter"))
1344 			canvas_item_editor->connect("mouse_enter", this, "_canvas_mouse_enter");
1345 		if (!canvas_item_editor->is_connected("mouse_exit", this, "_canvas_mouse_exit"))
1346 			canvas_item_editor->connect("mouse_exit", this, "_canvas_mouse_exit");
1347 
1348 		_update_palette();
1349 
1350 	} else {
1351 		node = NULL;
1352 
1353 		if (canvas_item_editor->is_connected("draw", this, "_canvas_draw"))
1354 			canvas_item_editor->disconnect("draw", this, "_canvas_draw");
1355 		if (canvas_item_editor->is_connected("mouse_enter", this, "_canvas_mouse_enter"))
1356 			canvas_item_editor->disconnect("mouse_enter", this, "_canvas_mouse_enter");
1357 		if (canvas_item_editor->is_connected("mouse_exit", this, "_canvas_mouse_exit"))
1358 			canvas_item_editor->disconnect("mouse_exit", this, "_canvas_mouse_exit");
1359 
1360 		_update_palette();
1361 	}
1362 
1363 	if (node)
1364 		node->connect("settings_changed", this, "_tileset_settings_changed");
1365 
1366 	_clear_bucket_cache();
1367 }
1368 
_tileset_settings_changed()1369 void TileMapEditor::_tileset_settings_changed() {
1370 
1371 	_update_palette();
1372 
1373 	if (canvas_item_editor)
1374 		canvas_item_editor->update();
1375 }
1376 
_icon_size_changed(float p_value)1377 void TileMapEditor::_icon_size_changed(float p_value) {
1378 	if (node) {
1379 		palette->set_icon_scale(p_value);
1380 		_update_palette();
1381 	}
1382 }
1383 
_bind_methods()1384 void TileMapEditor::_bind_methods() {
1385 
1386 	ObjectTypeDB::bind_method(_MD("_text_entered"), &TileMapEditor::_text_entered);
1387 	ObjectTypeDB::bind_method(_MD("_text_changed"), &TileMapEditor::_text_changed);
1388 	ObjectTypeDB::bind_method(_MD("_sbox_input"), &TileMapEditor::_sbox_input);
1389 	ObjectTypeDB::bind_method(_MD("_menu_option"), &TileMapEditor::_menu_option);
1390 	ObjectTypeDB::bind_method(_MD("_canvas_draw"), &TileMapEditor::_canvas_draw);
1391 	ObjectTypeDB::bind_method(_MD("_canvas_mouse_enter"), &TileMapEditor::_canvas_mouse_enter);
1392 	ObjectTypeDB::bind_method(_MD("_canvas_mouse_exit"), &TileMapEditor::_canvas_mouse_exit);
1393 	ObjectTypeDB::bind_method(_MD("_tileset_settings_changed"), &TileMapEditor::_tileset_settings_changed);
1394 	ObjectTypeDB::bind_method(_MD("_update_transform_buttons"), &TileMapEditor::_update_transform_buttons);
1395 
1396 	ObjectTypeDB::bind_method(_MD("_fill_points"), &TileMapEditor::_fill_points);
1397 	ObjectTypeDB::bind_method(_MD("_erase_points"), &TileMapEditor::_erase_points);
1398 
1399 	ObjectTypeDB::bind_method(_MD("_icon_size_changed"), &TileMapEditor::_icon_size_changed);
1400 }
1401 
_get_op_from_cell(const Point2i & p_pos)1402 TileMapEditor::CellOp TileMapEditor::_get_op_from_cell(const Point2i &p_pos) {
1403 	CellOp op;
1404 	op.idx = node->get_cell(p_pos.x, p_pos.y);
1405 	if (op.idx != TileMap::INVALID_CELL) {
1406 		if (node->is_cell_x_flipped(p_pos.x, p_pos.y))
1407 			op.xf = true;
1408 		if (node->is_cell_y_flipped(p_pos.x, p_pos.y))
1409 			op.yf = true;
1410 		if (node->is_cell_transposed(p_pos.x, p_pos.y))
1411 			op.tr = true;
1412 	}
1413 	return op;
1414 }
1415 
_update_transform_buttons(Object * p_button)1416 void TileMapEditor::_update_transform_buttons(Object *p_button) {
1417 	//ERR_FAIL_NULL(p_button);
1418 	ToolButton *b = p_button->cast_to<ToolButton>();
1419 	//ERR_FAIL_COND(!b);
1420 
1421 	if (b == rotate_0) {
1422 		mirror_x->set_pressed(false);
1423 		mirror_y->set_pressed(false);
1424 		transp->set_pressed(false);
1425 	} else if (b == rotate_90) {
1426 		mirror_x->set_pressed(true);
1427 		mirror_y->set_pressed(false);
1428 		transp->set_pressed(true);
1429 	} else if (b == rotate_180) {
1430 		mirror_x->set_pressed(true);
1431 		mirror_y->set_pressed(true);
1432 		transp->set_pressed(false);
1433 	} else if (b == rotate_270) {
1434 		mirror_x->set_pressed(false);
1435 		mirror_y->set_pressed(true);
1436 		transp->set_pressed(true);
1437 	}
1438 
1439 	flip_h = mirror_x->is_pressed();
1440 	flip_v = mirror_y->is_pressed();
1441 	transpose = transp->is_pressed();
1442 
1443 	rotate_0->set_pressed(!flip_h && !flip_v && !transpose);
1444 	rotate_90->set_pressed(flip_h && !flip_v && transpose);
1445 	rotate_180->set_pressed(flip_h && flip_v && !transpose);
1446 	rotate_270->set_pressed(!flip_h && flip_v && transpose);
1447 }
1448 
TileMapEditor(EditorNode * p_editor)1449 TileMapEditor::TileMapEditor(EditorNode *p_editor) {
1450 
1451 	node = NULL;
1452 	canvas_item_editor = NULL;
1453 	editor = p_editor;
1454 	undo_redo = editor->get_undo_redo();
1455 
1456 	tool = TOOL_NONE;
1457 	selection_active = false;
1458 	mouse_over = false;
1459 	show_tile_info = true;
1460 
1461 	flip_h = false;
1462 	flip_v = false;
1463 	transpose = false;
1464 
1465 	bucket_cache_tile = -1;
1466 	bucket_cache_visited = 0;
1467 
1468 	ED_SHORTCUT("tile_map_editor/erase_selection", TTR("Erase selection"), KEY_DELETE);
1469 	ED_SHORTCUT("tile_map_editor/find_tile", TTR("Find tile"), KEY_MASK_CMD + KEY_F);
1470 	ED_SHORTCUT("tile_map_editor/transpose", TTR("Transpose"));
1471 	ED_SHORTCUT("tile_map_editor/mirror_x", TTR("Mirror X"), KEY_A);
1472 	ED_SHORTCUT("tile_map_editor/mirror_y", TTR("Mirror Y"), KEY_S);
1473 
1474 	HBoxContainer *tool_hb1 = memnew(HBoxContainer);
1475 	add_child(tool_hb1);
1476 	HBoxContainer *tool_hb2 = memnew(HBoxContainer);
1477 	add_child(tool_hb2);
1478 
1479 	search_box = memnew(LineEdit);
1480 	search_box->set_h_size_flags(SIZE_EXPAND_FILL);
1481 	search_box->connect("text_entered", this, "_text_entered");
1482 	search_box->connect("text_changed", this, "_text_changed");
1483 	search_box->connect("input_event", this, "_sbox_input");
1484 	add_child(search_box);
1485 
1486 	size_slider = memnew(HSlider);
1487 	size_slider->set_h_size_flags(SIZE_EXPAND_FILL);
1488 	size_slider->set_min(0.1f);
1489 	size_slider->set_max(4.0f);
1490 	size_slider->set_step(0.1f);
1491 	size_slider->set_val(1.0f);
1492 	size_slider->connect("value_changed", this, "_icon_size_changed");
1493 	add_child(size_slider);
1494 
1495 	int mw = EDITOR_DEF("tile_map/palette_min_width", 80);
1496 
1497 	// Add tile palette
1498 	palette = memnew(ItemList);
1499 	palette->set_v_size_flags(SIZE_EXPAND_FILL);
1500 	palette->set_custom_minimum_size(Size2(mw, 0));
1501 	palette->set_max_columns(0);
1502 	palette->set_icon_mode(ItemList::ICON_MODE_TOP);
1503 	palette->set_max_text_lines(2);
1504 	add_child(palette);
1505 
1506 	// Add menu items
1507 	toolbar = memnew(HBoxContainer);
1508 	toolbar->set_h_size_flags(SIZE_EXPAND_FILL);
1509 	toolbar->set_alignment(BoxContainer::ALIGN_END);
1510 	toolbar->hide();
1511 	CanvasItemEditor::get_singleton()->add_control_to_menu_panel(toolbar);
1512 
1513 	// Tile position
1514 	tile_info = memnew(Label);
1515 	toolbar->add_child(tile_info);
1516 
1517 	options = memnew(MenuButton);
1518 	options->set_text("Tile Map");
1519 	options->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("TileMap", "EditorIcons"));
1520 	options->set_process_unhandled_key_input(false);
1521 
1522 	PopupMenu *p = options->get_popup();
1523 
1524 	p->add_item(TTR("Bucket"), OPTION_BUCKET);
1525 	p->add_separator();
1526 	p->add_item(TTR("Pick Tile"), OPTION_PICK_TILE, KEY_CONTROL);
1527 	p->add_separator();
1528 	p->add_shortcut(ED_SHORTCUT("tile_map_editor/select", TTR("Select"), KEY_MASK_CMD + KEY_B), OPTION_SELECT);
1529 	p->add_shortcut(ED_SHORTCUT("tile_map_editor/duplicate_selection", TTR("Duplicate Selection"), KEY_MASK_CMD + KEY_D), OPTION_DUPLICATE);
1530 	p->add_shortcut(ED_GET_SHORTCUT("tile_map_editor/erase_selection"), OPTION_ERASE_SELECTION);
1531 
1532 	p->connect("item_pressed", this, "_menu_option");
1533 
1534 	toolbar->add_child(options);
1535 
1536 	transp = memnew(ToolButton);
1537 	transp->set_toggle_mode(true);
1538 	transp->set_tooltip(TTR("Transpose") + " (" + ED_GET_SHORTCUT("tile_map_editor/transpose")->get_as_text() + ")");
1539 	transp->set_focus_mode(FOCUS_NONE);
1540 	transp->connect("pressed", this, "_update_transform_buttons", make_binds(transp));
1541 	tool_hb1->add_child(transp);
1542 	mirror_x = memnew(ToolButton);
1543 	mirror_x->set_toggle_mode(true);
1544 	mirror_x->set_tooltip(TTR("Mirror X") + " (" + ED_GET_SHORTCUT("tile_map_editor/mirror_x")->get_as_text() + ")");
1545 	mirror_x->set_focus_mode(FOCUS_NONE);
1546 	mirror_x->connect("pressed", this, "_update_transform_buttons", make_binds(mirror_x));
1547 	tool_hb1->add_child(mirror_x);
1548 	mirror_y = memnew(ToolButton);
1549 	mirror_y->set_toggle_mode(true);
1550 	mirror_y->set_tooltip(TTR("Mirror Y") + " (" + ED_GET_SHORTCUT("tile_map_editor/mirror_y")->get_as_text() + ")");
1551 	mirror_y->set_focus_mode(FOCUS_NONE);
1552 	mirror_y->connect("pressed", this, "_update_transform_buttons", make_binds(mirror_y));
1553 	tool_hb1->add_child(mirror_y);
1554 
1555 	rotate_0 = memnew(ToolButton);
1556 	rotate_0->set_toggle_mode(true);
1557 	rotate_0->set_tooltip(TTR("Rotate 0 degrees"));
1558 	rotate_0->set_focus_mode(FOCUS_NONE);
1559 	rotate_0->connect("pressed", this, "_update_transform_buttons", make_binds(rotate_0));
1560 	tool_hb2->add_child(rotate_0);
1561 	rotate_90 = memnew(ToolButton);
1562 	rotate_90->set_toggle_mode(true);
1563 	rotate_90->set_tooltip(TTR("Rotate 90 degrees"));
1564 	rotate_90->set_focus_mode(FOCUS_NONE);
1565 	rotate_90->connect("pressed", this, "_update_transform_buttons", make_binds(rotate_90));
1566 	tool_hb2->add_child(rotate_90);
1567 	rotate_180 = memnew(ToolButton);
1568 	rotate_180->set_toggle_mode(true);
1569 	rotate_180->set_tooltip(TTR("Rotate 180 degrees"));
1570 	rotate_180->set_focus_mode(FOCUS_NONE);
1571 	rotate_180->connect("pressed", this, "_update_transform_buttons", make_binds(rotate_180));
1572 	tool_hb2->add_child(rotate_180);
1573 	rotate_270 = memnew(ToolButton);
1574 	rotate_270->set_toggle_mode(true);
1575 	rotate_270->set_tooltip(TTR("Rotate 270 degrees"));
1576 	rotate_270->set_focus_mode(FOCUS_NONE);
1577 	rotate_270->connect("pressed", this, "_update_transform_buttons", make_binds(rotate_270));
1578 	tool_hb2->add_child(rotate_270);
1579 
1580 	rotate_0->set_pressed(true);
1581 }
1582 
~TileMapEditor()1583 TileMapEditor::~TileMapEditor() {
1584 	_clear_bucket_cache();
1585 }
1586 
1587 ///////////////////////////////////////////////////////////////
1588 ///////////////////////////////////////////////////////////////
1589 ///////////////////////////////////////////////////////////////
1590 
edit(Object * p_object)1591 void TileMapEditorPlugin::edit(Object *p_object) {
1592 
1593 	tile_map_editor->edit(p_object->cast_to<Node>());
1594 }
1595 
handles(Object * p_object) const1596 bool TileMapEditorPlugin::handles(Object *p_object) const {
1597 
1598 	return p_object->is_type("TileMap");
1599 }
1600 
make_visible(bool p_visible)1601 void TileMapEditorPlugin::make_visible(bool p_visible) {
1602 
1603 	if (p_visible) {
1604 
1605 		tile_map_editor->show();
1606 		tile_map_editor->get_toolbar()->show();
1607 	} else {
1608 
1609 		tile_map_editor->hide();
1610 		tile_map_editor->get_toolbar()->hide();
1611 		tile_map_editor->edit(NULL);
1612 	}
1613 }
1614 
TileMapEditorPlugin(EditorNode * p_node)1615 TileMapEditorPlugin::TileMapEditorPlugin(EditorNode *p_node) {
1616 
1617 	EDITOR_DEF("tile_map/preview_size", 64);
1618 	EDITOR_DEF("tile_map/palette_item_hseparation", 8);
1619 	EDITOR_DEF("tile_map/show_tile_names", true);
1620 	EDITOR_DEF("tile_map/show_tile_ids", true);
1621 	EDITOR_DEF("tile_map/sort_tiles_by_name", false);
1622 	EDITOR_DEF("tile_map/bucket_fill_preview", true);
1623 	EDITOR_DEF("tile_map/show_tile_info_on_hover", true);
1624 
1625 	tile_map_editor = memnew(TileMapEditor(p_node));
1626 	add_control_to_container(CONTAINER_CANVAS_EDITOR_SIDE, tile_map_editor);
1627 	tile_map_editor->hide();
1628 }
1629 
~TileMapEditorPlugin()1630 TileMapEditorPlugin::~TileMapEditorPlugin() {
1631 }
1632