1 | /**************************************************************************/ |
2 | /* tile_data_editors.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_data_editors.h" |
32 | |
33 | #include "tile_set_editor.h" |
34 | |
35 | #include "core/math/geometry_2d.h" |
36 | #include "core/os/keyboard.h" |
37 | |
38 | #include "editor/editor_node.h" |
39 | #include "editor/editor_properties.h" |
40 | #include "editor/editor_scale.h" |
41 | #include "editor/editor_settings.h" |
42 | #include "editor/editor_string_names.h" |
43 | #include "editor/editor_undo_redo_manager.h" |
44 | |
45 | #include "scene/gui/control.h" |
46 | #include "scene/gui/label.h" |
47 | #include "scene/gui/menu_button.h" |
48 | #include "scene/gui/option_button.h" |
49 | #include "scene/gui/separator.h" |
50 | #include "scene/gui/spin_box.h" |
51 | |
52 | #ifdef DEBUG_ENABLED |
53 | #include "servers/navigation_server_3d.h" |
54 | #endif // DEBUG_ENABLED |
55 | |
56 | void TileDataEditor::_tile_set_changed_plan_update() { |
57 | _tile_set_changed_update_needed = true; |
58 | call_deferred(SNAME("_tile_set_changed_deferred_update" )); |
59 | } |
60 | |
61 | void TileDataEditor::_tile_set_changed_deferred_update() { |
62 | if (_tile_set_changed_update_needed) { |
63 | _tile_set_changed(); |
64 | _tile_set_changed_update_needed = false; |
65 | } |
66 | } |
67 | |
68 | TileData *TileDataEditor::_get_tile_data(TileMapCell p_cell) { |
69 | ERR_FAIL_COND_V(!tile_set.is_valid(), nullptr); |
70 | ERR_FAIL_COND_V(!tile_set->has_source(p_cell.source_id), nullptr); |
71 | |
72 | TileData *td = nullptr; |
73 | TileSetSource *source = *tile_set->get_source(p_cell.source_id); |
74 | TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); |
75 | if (atlas_source) { |
76 | ERR_FAIL_COND_V(!atlas_source->has_tile(p_cell.get_atlas_coords()), nullptr); |
77 | ERR_FAIL_COND_V(!atlas_source->has_alternative_tile(p_cell.get_atlas_coords(), p_cell.alternative_tile), nullptr); |
78 | td = atlas_source->get_tile_data(p_cell.get_atlas_coords(), p_cell.alternative_tile); |
79 | } |
80 | |
81 | return td; |
82 | } |
83 | |
84 | void TileDataEditor::_bind_methods() { |
85 | ClassDB::bind_method(D_METHOD("_tile_set_changed_deferred_update" ), &TileDataEditor::_tile_set_changed_deferred_update); |
86 | |
87 | ADD_SIGNAL(MethodInfo("needs_redraw" )); |
88 | } |
89 | |
90 | void TileDataEditor::set_tile_set(Ref<TileSet> p_tile_set) { |
91 | if (tile_set.is_valid()) { |
92 | tile_set->disconnect_changed(callable_mp(this, &TileDataEditor::_tile_set_changed_plan_update)); |
93 | } |
94 | tile_set = p_tile_set; |
95 | if (tile_set.is_valid()) { |
96 | tile_set->connect_changed(callable_mp(this, &TileDataEditor::_tile_set_changed_plan_update)); |
97 | } |
98 | _tile_set_changed_plan_update(); |
99 | } |
100 | |
101 | bool DummyObject::_set(const StringName &p_name, const Variant &p_value) { |
102 | if (properties.has(p_name)) { |
103 | properties[p_name] = p_value; |
104 | return true; |
105 | } |
106 | return false; |
107 | } |
108 | |
109 | bool DummyObject::_get(const StringName &p_name, Variant &r_ret) const { |
110 | if (properties.has(p_name)) { |
111 | r_ret = properties[p_name]; |
112 | return true; |
113 | } |
114 | return false; |
115 | } |
116 | |
117 | bool DummyObject::has_dummy_property(StringName p_name) { |
118 | return properties.has(p_name); |
119 | } |
120 | |
121 | void DummyObject::add_dummy_property(StringName p_name) { |
122 | ERR_FAIL_COND(properties.has(p_name)); |
123 | properties[p_name] = Variant(); |
124 | } |
125 | |
126 | void DummyObject::remove_dummy_property(StringName p_name) { |
127 | ERR_FAIL_COND(!properties.has(p_name)); |
128 | properties.erase(p_name); |
129 | } |
130 | |
131 | void DummyObject::clear_dummy_properties() { |
132 | properties.clear(); |
133 | } |
134 | |
135 | void GenericTilePolygonEditor::_base_control_draw() { |
136 | ERR_FAIL_COND(!tile_set.is_valid()); |
137 | |
138 | real_t grab_threshold = EDITOR_GET("editors/polygon_editor/point_grab_radius" ); |
139 | |
140 | Color grid_color = EDITOR_GET("editors/tiles_editor/grid_color" ); |
141 | const Ref<Texture2D> handle = get_editor_theme_icon(SNAME("EditorPathSharpHandle" )); |
142 | const Ref<Texture2D> add_handle = get_editor_theme_icon(SNAME("EditorHandleAdd" )); |
143 | const Ref<StyleBox> focus_stylebox = get_theme_stylebox(SNAME("Focus" ), EditorStringName(EditorStyles)); |
144 | |
145 | // Draw the focus rectangle. |
146 | if (base_control->has_focus()) { |
147 | base_control->draw_style_box(focus_stylebox, Rect2(Vector2(), base_control->get_size())); |
148 | } |
149 | |
150 | // Draw tile-related things. |
151 | Size2 tile_size = tile_set->get_tile_size(); |
152 | |
153 | Transform2D xform; |
154 | xform.set_origin(base_control->get_size() / 2 + panning); |
155 | xform.set_scale(Vector2(editor_zoom_widget->get_zoom(), editor_zoom_widget->get_zoom())); |
156 | base_control->draw_set_transform_matrix(xform); |
157 | |
158 | // Draw the tile shape filled. |
159 | Transform2D tile_xform; |
160 | tile_xform.set_scale(tile_size); |
161 | tile_set->draw_tile_shape(base_control, tile_xform, Color(1.0, 1.0, 1.0, 0.3), true); |
162 | |
163 | // Draw the background. |
164 | if (background_texture.is_valid()) { |
165 | Size2 region_size = background_region.size; |
166 | if (background_h_flip) { |
167 | region_size.x = -region_size.x; |
168 | } |
169 | if (background_v_flip) { |
170 | region_size.y = -region_size.y; |
171 | } |
172 | base_control->draw_texture_rect_region(background_texture, Rect2(-background_region.size / 2 - background_offset, region_size), background_region, background_modulate, background_transpose); |
173 | } |
174 | |
175 | // Draw grid. |
176 | if (current_snap_option == SNAP_GRID) { |
177 | Vector2 spacing = tile_size / snap_subdivision->get_value(); |
178 | Vector2 offset = -tile_size / 2; |
179 | |
180 | for (int i = 1; i < snap_subdivision->get_value(); i++) { |
181 | base_control->draw_line(Vector2(spacing.x * i, 0) + offset, Vector2(spacing.x * i, tile_size.y) + offset, Color(1, 1, 1, 0.33)); |
182 | base_control->draw_line(Vector2(0, spacing.y * i) + offset, Vector2(tile_size.x, spacing.y * i) + offset, Color(1, 1, 1, 0.33)); |
183 | } |
184 | } |
185 | |
186 | // Draw the polygons. |
187 | for (const Vector<Vector2> &polygon : polygons) { |
188 | Color color = polygon_color; |
189 | if (!in_creation_polygon.is_empty()) { |
190 | color = color.darkened(0.3); |
191 | } |
192 | color.a = 0.5; |
193 | Vector<Color> v_color; |
194 | v_color.push_back(color); |
195 | base_control->draw_polygon(polygon, v_color); |
196 | |
197 | color.a = 0.7; |
198 | for (int j = 0; j < polygon.size(); j++) { |
199 | base_control->draw_line(polygon[j], polygon[(j + 1) % polygon.size()], color); |
200 | } |
201 | } |
202 | |
203 | // Draw the polygon in creation. |
204 | if (!in_creation_polygon.is_empty()) { |
205 | for (int i = 0; i < in_creation_polygon.size() - 1; i++) { |
206 | base_control->draw_line(in_creation_polygon[i], in_creation_polygon[i + 1], Color(1.0, 1.0, 1.0)); |
207 | } |
208 | } |
209 | |
210 | Point2 in_creation_point = xform.affine_inverse().xform(base_control->get_local_mouse_position()); |
211 | float in_creation_distance = grab_threshold * 2.0; |
212 | _snap_to_tile_shape(in_creation_point, in_creation_distance, grab_threshold / editor_zoom_widget->get_zoom()); |
213 | _snap_point(in_creation_point); |
214 | |
215 | if (drag_type == DRAG_TYPE_CREATE_POINT && !in_creation_polygon.is_empty()) { |
216 | base_control->draw_line(in_creation_polygon[in_creation_polygon.size() - 1], in_creation_point, Color(1.0, 1.0, 1.0)); |
217 | } |
218 | |
219 | // Draw the handles. |
220 | int tinted_polygon_index = -1; |
221 | int tinted_point_index = -1; |
222 | if (drag_type == DRAG_TYPE_DRAG_POINT) { |
223 | tinted_polygon_index = drag_polygon_index; |
224 | tinted_point_index = drag_point_index; |
225 | } else if (hovered_point_index >= 0) { |
226 | tinted_polygon_index = hovered_polygon_index; |
227 | tinted_point_index = hovered_point_index; |
228 | } |
229 | |
230 | base_control->draw_set_transform_matrix(Transform2D()); |
231 | if (!in_creation_polygon.is_empty()) { |
232 | for (int i = 0; i < in_creation_polygon.size(); i++) { |
233 | base_control->draw_texture(handle, xform.xform(in_creation_polygon[i]) - handle->get_size() / 2); |
234 | } |
235 | } else { |
236 | for (int i = 0; i < (int)polygons.size(); i++) { |
237 | const Vector<Vector2> &polygon = polygons[i]; |
238 | for (int j = 0; j < polygon.size(); j++) { |
239 | const Color poly_modulate = (tinted_polygon_index == i && tinted_point_index == j) ? Color(0.5, 1, 2) : Color(1, 1, 1); |
240 | base_control->draw_texture(handle, xform.xform(polygon[j]) - handle->get_size() / 2, poly_modulate); |
241 | } |
242 | } |
243 | } |
244 | |
245 | // Draw the text on top of the selected point. |
246 | if (tinted_polygon_index >= 0) { |
247 | Ref<Font> font = get_theme_font(SNAME("font" ), SNAME("Label" )); |
248 | int font_size = get_theme_font_size(SNAME("font_size" ), SNAME("Label" )); |
249 | String text = multiple_polygon_mode ? vformat("%d:%d" , tinted_polygon_index, tinted_point_index) : vformat("%d" , tinted_point_index); |
250 | Size2 text_size = font->get_string_size(text, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size); |
251 | base_control->draw_string(font, xform.xform(polygons[tinted_polygon_index][tinted_point_index]) - text_size * 0.5, text, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, Color(1.0, 1.0, 1.0, 0.5)); |
252 | } |
253 | |
254 | if (drag_type == DRAG_TYPE_CREATE_POINT) { |
255 | base_control->draw_texture(handle, xform.xform(in_creation_point) - handle->get_size() / 2, Color(0.5, 1, 2)); |
256 | } |
257 | |
258 | // Draw the point creation preview in edit mode. |
259 | if (hovered_segment_index >= 0) { |
260 | base_control->draw_texture(add_handle, xform.xform(hovered_segment_point) - add_handle->get_size() / 2); |
261 | } |
262 | |
263 | // Draw the tile shape line. |
264 | base_control->draw_set_transform_matrix(xform); |
265 | tile_set->draw_tile_shape(base_control, tile_xform, grid_color, false); |
266 | base_control->draw_set_transform_matrix(Transform2D()); |
267 | } |
268 | |
269 | void GenericTilePolygonEditor::_center_view() { |
270 | panning = Vector2(); |
271 | base_control->queue_redraw(); |
272 | button_center_view->set_disabled(true); |
273 | } |
274 | |
275 | void GenericTilePolygonEditor::_zoom_changed() { |
276 | base_control->queue_redraw(); |
277 | } |
278 | |
279 | void GenericTilePolygonEditor::(int p_item_pressed) { |
280 | EditorUndoRedoManager *undo_redo; |
281 | if (use_undo_redo) { |
282 | undo_redo = EditorUndoRedoManager::get_singleton(); |
283 | } else { |
284 | // This nice hack allows for discarding undo actions without making code too complex. |
285 | undo_redo = memnew(EditorUndoRedoManager); |
286 | } |
287 | |
288 | switch (p_item_pressed) { |
289 | case RESET_TO_DEFAULT_TILE: { |
290 | undo_redo->create_action(TTR("Reset Polygons" )); |
291 | undo_redo->add_do_method(this, "clear_polygons" ); |
292 | Vector<Vector2> polygon = tile_set->get_tile_shape_polygon(); |
293 | for (int i = 0; i < polygon.size(); i++) { |
294 | polygon.write[i] = polygon[i] * tile_set->get_tile_size(); |
295 | } |
296 | undo_redo->add_do_method(this, "add_polygon" , polygon); |
297 | undo_redo->add_do_method(base_control, "queue_redraw" ); |
298 | undo_redo->add_do_method(this, "emit_signal" , "polygons_changed" ); |
299 | undo_redo->add_undo_method(this, "clear_polygons" ); |
300 | for (const PackedVector2Array &poly : polygons) { |
301 | undo_redo->add_undo_method(this, "add_polygon" , poly); |
302 | } |
303 | undo_redo->add_undo_method(base_control, "queue_redraw" ); |
304 | undo_redo->add_undo_method(this, "emit_signal" , "polygons_changed" ); |
305 | undo_redo->commit_action(true); |
306 | } break; |
307 | case CLEAR_TILE: { |
308 | undo_redo->create_action(TTR("Clear Polygons" )); |
309 | undo_redo->add_do_method(this, "clear_polygons" ); |
310 | undo_redo->add_do_method(base_control, "queue_redraw" ); |
311 | undo_redo->add_do_method(this, "emit_signal" , "polygons_changed" ); |
312 | undo_redo->add_undo_method(this, "clear_polygons" ); |
313 | for (const PackedVector2Array &polygon : polygons) { |
314 | undo_redo->add_undo_method(this, "add_polygon" , polygon); |
315 | } |
316 | undo_redo->add_undo_method(base_control, "queue_redraw" ); |
317 | undo_redo->add_undo_method(this, "emit_signal" , "polygons_changed" ); |
318 | undo_redo->commit_action(true); |
319 | } break; |
320 | case ROTATE_RIGHT: |
321 | case ROTATE_LEFT: |
322 | case FLIP_HORIZONTALLY: |
323 | case FLIP_VERTICALLY: { |
324 | switch (p_item_pressed) { |
325 | case ROTATE_RIGHT: { |
326 | undo_redo->create_action(TTR("Rotate Polygons Right" )); |
327 | } break; |
328 | case ROTATE_LEFT: { |
329 | undo_redo->create_action(TTR("Rotate Polygons Left" )); |
330 | } break; |
331 | case FLIP_HORIZONTALLY: { |
332 | undo_redo->create_action(TTR("Flip Polygons Horizontally" )); |
333 | } break; |
334 | case FLIP_VERTICALLY: { |
335 | undo_redo->create_action(TTR("Flip Polygons Vertically" )); |
336 | } break; |
337 | default: |
338 | break; |
339 | } |
340 | for (unsigned int i = 0; i < polygons.size(); i++) { |
341 | Vector<Point2> new_polygon; |
342 | for (const Vector2 &vec : polygons[i]) { |
343 | Vector2 point = vec; |
344 | switch (p_item_pressed) { |
345 | case ROTATE_RIGHT: { |
346 | point = Vector2(-point.y, point.x); |
347 | } break; |
348 | case ROTATE_LEFT: { |
349 | point = Vector2(point.y, -point.x); |
350 | } break; |
351 | case FLIP_HORIZONTALLY: { |
352 | point = Vector2(-point.x, point.y); |
353 | } break; |
354 | case FLIP_VERTICALLY: { |
355 | point = Vector2(point.x, -point.y); |
356 | } break; |
357 | default: |
358 | break; |
359 | } |
360 | new_polygon.push_back(point); |
361 | } |
362 | undo_redo->add_do_method(this, "set_polygon" , i, new_polygon); |
363 | } |
364 | undo_redo->add_do_method(base_control, "queue_redraw" ); |
365 | undo_redo->add_do_method(this, "emit_signal" , "polygons_changed" ); |
366 | for (const PackedVector2Array &polygon : polygons) { |
367 | undo_redo->add_undo_method(this, "set_polygon" , polygon); |
368 | } |
369 | undo_redo->add_undo_method(base_control, "queue_redraw" ); |
370 | undo_redo->add_undo_method(this, "emit_signal" , "polygons_changed" ); |
371 | undo_redo->commit_action(true); |
372 | } break; |
373 | default: |
374 | break; |
375 | } |
376 | |
377 | if (!use_undo_redo) { |
378 | memdelete(undo_redo); |
379 | } |
380 | } |
381 | |
382 | void GenericTilePolygonEditor::_grab_polygon_point(Vector2 p_pos, const Transform2D &p_polygon_xform, int &r_polygon_index, int &r_point_index) { |
383 | const real_t grab_threshold = EDITOR_GET("editors/polygon_editor/point_grab_radius" ); |
384 | r_polygon_index = -1; |
385 | r_point_index = -1; |
386 | float closest_distance = grab_threshold + 1.0; |
387 | for (unsigned int i = 0; i < polygons.size(); i++) { |
388 | const Vector<Vector2> &polygon = polygons[i]; |
389 | for (int j = 0; j < polygon.size(); j++) { |
390 | float distance = p_pos.distance_to(p_polygon_xform.xform(polygon[j])); |
391 | if (distance < grab_threshold && distance < closest_distance) { |
392 | r_polygon_index = i; |
393 | r_point_index = j; |
394 | closest_distance = distance; |
395 | } |
396 | } |
397 | } |
398 | } |
399 | |
400 | void GenericTilePolygonEditor::_grab_polygon_segment_point(Vector2 p_pos, const Transform2D &p_polygon_xform, int &r_polygon_index, int &r_segment_index, Vector2 &r_point) { |
401 | const real_t grab_threshold = EDITOR_GET("editors/polygon_editor/point_grab_radius" ); |
402 | |
403 | Point2 point = p_polygon_xform.affine_inverse().xform(p_pos); |
404 | r_polygon_index = -1; |
405 | r_segment_index = -1; |
406 | float closest_distance = grab_threshold * 2.0; |
407 | for (unsigned int i = 0; i < polygons.size(); i++) { |
408 | const Vector<Vector2> &polygon = polygons[i]; |
409 | for (int j = 0; j < polygon.size(); j++) { |
410 | Vector2 segment[2] = { polygon[j], polygon[(j + 1) % polygon.size()] }; |
411 | Vector2 closest_point = Geometry2D::get_closest_point_to_segment(point, segment); |
412 | float distance = closest_point.distance_to(point); |
413 | if (distance < grab_threshold / editor_zoom_widget->get_zoom() && distance < closest_distance) { |
414 | r_polygon_index = i; |
415 | r_segment_index = j; |
416 | r_point = closest_point; |
417 | closest_distance = distance; |
418 | } |
419 | } |
420 | } |
421 | } |
422 | |
423 | void GenericTilePolygonEditor::_snap_to_tile_shape(Point2 &r_point, float &r_current_snapped_dist, float p_snap_dist) { |
424 | ERR_FAIL_COND(!tile_set.is_valid()); |
425 | |
426 | Vector<Point2> polygon = tile_set->get_tile_shape_polygon(); |
427 | for (int i = 0; i < polygon.size(); i++) { |
428 | polygon.write[i] = polygon[i] * tile_set->get_tile_size(); |
429 | } |
430 | Point2 snapped_point = r_point; |
431 | |
432 | // Snap to polygon vertices. |
433 | bool snapped = false; |
434 | for (int i = 0; i < polygon.size(); i++) { |
435 | float distance = r_point.distance_to(polygon[i]); |
436 | if (distance < p_snap_dist && distance < r_current_snapped_dist) { |
437 | snapped_point = polygon[i]; |
438 | r_current_snapped_dist = distance; |
439 | snapped = true; |
440 | } |
441 | } |
442 | |
443 | // Snap to edges if we did not snap to vertices. |
444 | if (!snapped) { |
445 | for (int i = 0; i < polygon.size(); i++) { |
446 | Point2 segment[2] = { polygon[i], polygon[(i + 1) % polygon.size()] }; |
447 | Point2 point = Geometry2D::get_closest_point_to_segment(r_point, segment); |
448 | float distance = r_point.distance_to(point); |
449 | if (distance < p_snap_dist && distance < r_current_snapped_dist) { |
450 | snapped_point = point; |
451 | r_current_snapped_dist = distance; |
452 | } |
453 | } |
454 | } |
455 | |
456 | r_point = snapped_point; |
457 | } |
458 | |
459 | void GenericTilePolygonEditor::_snap_point(Point2 &r_point) { |
460 | switch (current_snap_option) { |
461 | case SNAP_NONE: |
462 | break; |
463 | |
464 | case SNAP_HALF_PIXEL: |
465 | r_point = r_point.snapped(Vector2(0.5, 0.5)); |
466 | break; |
467 | |
468 | case SNAP_GRID: { |
469 | const Vector2 tile_size = tile_set->get_tile_size(); |
470 | r_point = (r_point + tile_size / 2).snapped(tile_size / snap_subdivision->get_value()) - tile_size / 2; |
471 | } break; |
472 | } |
473 | } |
474 | |
475 | void GenericTilePolygonEditor::_base_control_gui_input(Ref<InputEvent> p_event) { |
476 | EditorUndoRedoManager *undo_redo; |
477 | if (use_undo_redo) { |
478 | undo_redo = EditorUndoRedoManager::get_singleton(); |
479 | } else { |
480 | // This nice hack allows for discarding undo actions without making code too complex. |
481 | undo_redo = memnew(EditorUndoRedoManager); |
482 | } |
483 | |
484 | real_t grab_threshold = EDITOR_GET("editors/polygon_editor/point_grab_radius" ); |
485 | |
486 | hovered_polygon_index = -1; |
487 | hovered_point_index = -1; |
488 | hovered_segment_index = -1; |
489 | hovered_segment_point = Vector2(); |
490 | |
491 | Transform2D xform; |
492 | xform.set_origin(base_control->get_size() / 2 + panning); |
493 | xform.set_scale(Vector2(editor_zoom_widget->get_zoom(), editor_zoom_widget->get_zoom())); |
494 | |
495 | Ref<InputEventMouseMotion> mm = p_event; |
496 | if (mm.is_valid()) { |
497 | if (drag_type == DRAG_TYPE_DRAG_POINT) { |
498 | ERR_FAIL_INDEX(drag_polygon_index, (int)polygons.size()); |
499 | ERR_FAIL_INDEX(drag_point_index, polygons[drag_polygon_index].size()); |
500 | Point2 point = xform.affine_inverse().xform(mm->get_position()); |
501 | float distance = grab_threshold * 2.0; |
502 | _snap_to_tile_shape(point, distance, grab_threshold / editor_zoom_widget->get_zoom()); |
503 | _snap_point(point); |
504 | polygons[drag_polygon_index].write[drag_point_index] = point; |
505 | } else if (drag_type == DRAG_TYPE_PAN) { |
506 | panning += mm->get_position() - drag_last_pos; |
507 | drag_last_pos = mm->get_position(); |
508 | button_center_view->set_disabled(panning.is_zero_approx()); |
509 | } else { |
510 | // Update hovered point. |
511 | _grab_polygon_point(mm->get_position(), xform, hovered_polygon_index, hovered_point_index); |
512 | |
513 | // If we have no hovered point, check if we hover a segment. |
514 | if (hovered_point_index == -1) { |
515 | _grab_polygon_segment_point(mm->get_position(), xform, hovered_polygon_index, hovered_segment_index, hovered_segment_point); |
516 | } |
517 | } |
518 | } |
519 | |
520 | Ref<InputEventMouseButton> mb = p_event; |
521 | if (mb.is_valid()) { |
522 | if (mb->get_button_index() == MouseButton::WHEEL_UP && mb->is_command_or_control_pressed()) { |
523 | editor_zoom_widget->set_zoom_by_increments(1); |
524 | _zoom_changed(); |
525 | accept_event(); |
526 | } else if (mb->get_button_index() == MouseButton::WHEEL_DOWN && mb->is_command_or_control_pressed()) { |
527 | editor_zoom_widget->set_zoom_by_increments(-1); |
528 | _zoom_changed(); |
529 | accept_event(); |
530 | } else if (mb->get_button_index() == MouseButton::LEFT) { |
531 | if (mb->is_pressed()) { |
532 | if (tools_button_group->get_pressed_button() != button_create) { |
533 | in_creation_polygon.clear(); |
534 | } |
535 | if (tools_button_group->get_pressed_button() == button_create) { |
536 | // Create points. |
537 | if (in_creation_polygon.size() >= 3 && mb->get_position().distance_to(xform.xform(in_creation_polygon[0])) < grab_threshold) { |
538 | // Closes and create polygon. |
539 | if (!multiple_polygon_mode) { |
540 | clear_polygons(); |
541 | } |
542 | int added = add_polygon(in_creation_polygon); |
543 | |
544 | in_creation_polygon.clear(); |
545 | button_edit->set_pressed(true); |
546 | undo_redo->create_action(TTR("Edit Polygons" )); |
547 | if (!multiple_polygon_mode) { |
548 | undo_redo->add_do_method(this, "clear_polygons" ); |
549 | } |
550 | undo_redo->add_do_method(this, "add_polygon" , in_creation_polygon); |
551 | undo_redo->add_do_method(base_control, "queue_redraw" ); |
552 | undo_redo->add_undo_method(this, "remove_polygon" , added); |
553 | undo_redo->add_undo_method(base_control, "queue_redraw" ); |
554 | undo_redo->commit_action(false); |
555 | emit_signal(SNAME("polygons_changed" )); |
556 | } else { |
557 | // Create a new point. |
558 | drag_type = DRAG_TYPE_CREATE_POINT; |
559 | } |
560 | } else if (tools_button_group->get_pressed_button() == button_edit) { |
561 | // Edit points. |
562 | int closest_polygon; |
563 | int closest_point; |
564 | _grab_polygon_point(mb->get_position(), xform, closest_polygon, closest_point); |
565 | if (closest_polygon >= 0) { |
566 | drag_type = DRAG_TYPE_DRAG_POINT; |
567 | drag_polygon_index = closest_polygon; |
568 | drag_point_index = closest_point; |
569 | drag_old_polygon = polygons[drag_polygon_index]; |
570 | } else { |
571 | // Create a point. |
572 | Vector2 point_to_create; |
573 | _grab_polygon_segment_point(mb->get_position(), xform, closest_polygon, closest_point, point_to_create); |
574 | if (closest_polygon >= 0) { |
575 | polygons[closest_polygon].insert(closest_point + 1, point_to_create); |
576 | drag_type = DRAG_TYPE_DRAG_POINT; |
577 | drag_polygon_index = closest_polygon; |
578 | drag_point_index = closest_point + 1; |
579 | drag_old_polygon = polygons[closest_polygon]; |
580 | } |
581 | } |
582 | } else if (tools_button_group->get_pressed_button() == button_delete) { |
583 | // Remove point. |
584 | int closest_polygon; |
585 | int closest_point; |
586 | _grab_polygon_point(mb->get_position(), xform, closest_polygon, closest_point); |
587 | if (closest_polygon >= 0) { |
588 | PackedVector2Array old_polygon = polygons[closest_polygon]; |
589 | polygons[closest_polygon].remove_at(closest_point); |
590 | undo_redo->create_action(TTR("Edit Polygons" )); |
591 | if (polygons[closest_polygon].size() < 3) { |
592 | remove_polygon(closest_polygon); |
593 | undo_redo->add_do_method(this, "remove_polygon" , closest_polygon); |
594 | undo_redo->add_undo_method(this, "add_polygon" , old_polygon, closest_polygon); |
595 | } else { |
596 | undo_redo->add_do_method(this, "set_polygon" , closest_polygon, polygons[closest_polygon]); |
597 | undo_redo->add_undo_method(this, "set_polygon" , closest_polygon, old_polygon); |
598 | } |
599 | undo_redo->add_do_method(base_control, "queue_redraw" ); |
600 | undo_redo->add_undo_method(base_control, "queue_redraw" ); |
601 | undo_redo->commit_action(false); |
602 | emit_signal(SNAME("polygons_changed" )); |
603 | } |
604 | } |
605 | } else { |
606 | if (drag_type == DRAG_TYPE_DRAG_POINT) { |
607 | undo_redo->create_action(TTR("Edit Polygons" )); |
608 | undo_redo->add_do_method(this, "set_polygon" , drag_polygon_index, polygons[drag_polygon_index]); |
609 | undo_redo->add_do_method(base_control, "queue_redraw" ); |
610 | undo_redo->add_undo_method(this, "set_polygon" , drag_polygon_index, drag_old_polygon); |
611 | undo_redo->add_undo_method(base_control, "queue_redraw" ); |
612 | undo_redo->commit_action(false); |
613 | emit_signal(SNAME("polygons_changed" )); |
614 | } else if (drag_type == DRAG_TYPE_CREATE_POINT) { |
615 | Point2 point = xform.affine_inverse().xform(mb->get_position()); |
616 | float distance = grab_threshold * 2; |
617 | _snap_to_tile_shape(point, distance, grab_threshold / editor_zoom_widget->get_zoom()); |
618 | _snap_point(point); |
619 | in_creation_polygon.push_back(point); |
620 | } |
621 | drag_type = DRAG_TYPE_NONE; |
622 | drag_point_index = -1; |
623 | } |
624 | |
625 | } else if (mb->get_button_index() == MouseButton::RIGHT) { |
626 | if (mb->is_pressed()) { |
627 | if (tools_button_group->get_pressed_button() == button_edit) { |
628 | // Remove point or pan. |
629 | int closest_polygon; |
630 | int closest_point; |
631 | _grab_polygon_point(mb->get_position(), xform, closest_polygon, closest_point); |
632 | if (closest_polygon >= 0) { |
633 | PackedVector2Array old_polygon = polygons[closest_polygon]; |
634 | polygons[closest_polygon].remove_at(closest_point); |
635 | undo_redo->create_action(TTR("Edit Polygons" )); |
636 | if (polygons[closest_polygon].size() < 3) { |
637 | remove_polygon(closest_polygon); |
638 | undo_redo->add_do_method(this, "remove_polygon" , closest_polygon); |
639 | undo_redo->add_undo_method(this, "add_polygon" , old_polygon, closest_polygon); |
640 | } else { |
641 | undo_redo->add_do_method(this, "set_polygon" , closest_polygon, polygons[closest_polygon]); |
642 | undo_redo->add_undo_method(this, "set_polygon" , closest_polygon, old_polygon); |
643 | } |
644 | undo_redo->add_do_method(base_control, "queue_redraw" ); |
645 | undo_redo->add_undo_method(base_control, "queue_redraw" ); |
646 | undo_redo->commit_action(false); |
647 | emit_signal(SNAME("polygons_changed" )); |
648 | } else { |
649 | drag_type = DRAG_TYPE_PAN; |
650 | drag_last_pos = mb->get_position(); |
651 | } |
652 | } else { |
653 | drag_type = DRAG_TYPE_PAN; |
654 | drag_last_pos = mb->get_position(); |
655 | } |
656 | } else { |
657 | drag_type = DRAG_TYPE_NONE; |
658 | } |
659 | } else if (mb->get_button_index() == MouseButton::MIDDLE) { |
660 | if (mb->is_pressed()) { |
661 | drag_type = DRAG_TYPE_PAN; |
662 | drag_last_pos = mb->get_position(); |
663 | } else { |
664 | drag_type = DRAG_TYPE_NONE; |
665 | } |
666 | } |
667 | } |
668 | |
669 | base_control->queue_redraw(); |
670 | |
671 | if (!use_undo_redo) { |
672 | memdelete(undo_redo); |
673 | } |
674 | } |
675 | |
676 | void GenericTilePolygonEditor::_set_snap_option(int p_index) { |
677 | current_snap_option = p_index; |
678 | button_pixel_snap->set_icon(button_pixel_snap->get_popup()->get_item_icon(p_index)); |
679 | snap_subdivision->set_visible(p_index == SNAP_GRID); |
680 | base_control->queue_redraw(); |
681 | _store_snap_options(); |
682 | } |
683 | |
684 | void GenericTilePolygonEditor::_store_snap_options() { |
685 | EditorSettings::get_singleton()->set_project_metadata("editor_metadata" , "tile_snap_option" , current_snap_option); |
686 | EditorSettings::get_singleton()->set_project_metadata("editor_metadata" , "tile_snap_subdiv" , snap_subdivision->get_value()); |
687 | } |
688 | |
689 | void GenericTilePolygonEditor::_toggle_expand(bool p_expand) { |
690 | if (p_expand) { |
691 | TileSetEditor::get_singleton()->add_expanded_editor(this); |
692 | } else { |
693 | TileSetEditor::get_singleton()->remove_expanded_editor(); |
694 | } |
695 | } |
696 | |
697 | void GenericTilePolygonEditor::set_use_undo_redo(bool p_use_undo_redo) { |
698 | use_undo_redo = p_use_undo_redo; |
699 | } |
700 | |
701 | void GenericTilePolygonEditor::set_tile_set(Ref<TileSet> p_tile_set) { |
702 | ERR_FAIL_COND(!p_tile_set.is_valid()); |
703 | if (tile_set == p_tile_set) { |
704 | return; |
705 | } |
706 | |
707 | // Set the default tile shape |
708 | clear_polygons(); |
709 | if (p_tile_set.is_valid()) { |
710 | Vector<Vector2> polygon = p_tile_set->get_tile_shape_polygon(); |
711 | for (int i = 0; i < polygon.size(); i++) { |
712 | polygon.write[i] = polygon[i] * p_tile_set->get_tile_size(); |
713 | } |
714 | add_polygon(polygon); |
715 | } |
716 | |
717 | tile_set = p_tile_set; |
718 | |
719 | // Set the default zoom value. |
720 | int default_control_y_size = 200 * EDSCALE; |
721 | Vector2 zoomed_tile = editor_zoom_widget->get_zoom() * tile_set->get_tile_size(); |
722 | while (zoomed_tile.y < default_control_y_size) { |
723 | editor_zoom_widget->set_zoom_by_increments(6, false); |
724 | zoomed_tile = editor_zoom_widget->get_zoom() * tile_set->get_tile_size(); |
725 | } |
726 | while (zoomed_tile.y > default_control_y_size) { |
727 | editor_zoom_widget->set_zoom_by_increments(-6, false); |
728 | zoomed_tile = editor_zoom_widget->get_zoom() * tile_set->get_tile_size(); |
729 | } |
730 | editor_zoom_widget->set_zoom_by_increments(-6, false); |
731 | _zoom_changed(); |
732 | } |
733 | |
734 | void GenericTilePolygonEditor::set_background(Ref<Texture2D> p_texture, Rect2 p_region, Vector2 p_offset, bool p_flip_h, bool p_flip_v, bool p_transpose, Color p_modulate) { |
735 | background_texture = p_texture; |
736 | background_region = p_region; |
737 | background_offset = p_offset; |
738 | background_h_flip = p_flip_h; |
739 | background_v_flip = p_flip_v; |
740 | background_transpose = p_transpose; |
741 | background_modulate = p_modulate; |
742 | base_control->queue_redraw(); |
743 | } |
744 | |
745 | int GenericTilePolygonEditor::get_polygon_count() { |
746 | return polygons.size(); |
747 | } |
748 | |
749 | int GenericTilePolygonEditor::add_polygon(Vector<Point2> p_polygon, int p_index) { |
750 | ERR_FAIL_COND_V(p_polygon.size() < 3, -1); |
751 | ERR_FAIL_COND_V(!multiple_polygon_mode && polygons.size() >= 1, -1); |
752 | |
753 | if (p_index < 0) { |
754 | polygons.push_back(p_polygon); |
755 | base_control->queue_redraw(); |
756 | button_edit->set_pressed(true); |
757 | return polygons.size() - 1; |
758 | } else { |
759 | polygons.insert(p_index, p_polygon); |
760 | button_edit->set_pressed(true); |
761 | base_control->queue_redraw(); |
762 | return p_index; |
763 | } |
764 | } |
765 | |
766 | void GenericTilePolygonEditor::remove_polygon(int p_index) { |
767 | ERR_FAIL_INDEX(p_index, (int)polygons.size()); |
768 | polygons.remove_at(p_index); |
769 | |
770 | if (polygons.size() == 0) { |
771 | button_create->set_pressed(true); |
772 | } |
773 | base_control->queue_redraw(); |
774 | } |
775 | |
776 | void GenericTilePolygonEditor::clear_polygons() { |
777 | polygons.clear(); |
778 | base_control->queue_redraw(); |
779 | } |
780 | |
781 | void GenericTilePolygonEditor::set_polygon(int p_polygon_index, Vector<Point2> p_polygon) { |
782 | ERR_FAIL_INDEX(p_polygon_index, (int)polygons.size()); |
783 | ERR_FAIL_COND(p_polygon.size() < 3); |
784 | polygons[p_polygon_index] = p_polygon; |
785 | button_edit->set_pressed(true); |
786 | base_control->queue_redraw(); |
787 | } |
788 | |
789 | Vector<Point2> GenericTilePolygonEditor::get_polygon(int p_polygon_index) { |
790 | ERR_FAIL_INDEX_V(p_polygon_index, (int)polygons.size(), Vector<Point2>()); |
791 | return polygons[p_polygon_index]; |
792 | } |
793 | |
794 | void GenericTilePolygonEditor::set_polygons_color(Color p_color) { |
795 | polygon_color = p_color; |
796 | base_control->queue_redraw(); |
797 | } |
798 | |
799 | void GenericTilePolygonEditor::set_multiple_polygon_mode(bool p_multiple_polygon_mode) { |
800 | multiple_polygon_mode = p_multiple_polygon_mode; |
801 | } |
802 | |
803 | void GenericTilePolygonEditor::_notification(int p_what) { |
804 | switch (p_what) { |
805 | case NOTIFICATION_ENTER_TREE: { |
806 | if (!get_meta("reparented" , false)) { |
807 | button_expand->set_pressed_no_signal(false); |
808 | } |
809 | } break; |
810 | case NOTIFICATION_THEME_CHANGED: { |
811 | button_expand->set_icon(get_editor_theme_icon(SNAME("DistractionFree" ))); |
812 | button_create->set_icon(get_editor_theme_icon(SNAME("CurveCreate" ))); |
813 | button_edit->set_icon(get_editor_theme_icon(SNAME("CurveEdit" ))); |
814 | button_delete->set_icon(get_editor_theme_icon(SNAME("CurveDelete" ))); |
815 | button_center_view->set_icon(get_editor_theme_icon(SNAME("CenterView" ))); |
816 | button_advanced_menu->set_icon(get_editor_theme_icon(SNAME("GuiTabMenuHl" ))); |
817 | button_pixel_snap->get_popup()->set_item_icon(0, get_editor_theme_icon(SNAME("SnapDisable" ))); |
818 | button_pixel_snap->get_popup()->set_item_icon(1, get_editor_theme_icon(SNAME("Snap" ))); |
819 | button_pixel_snap->get_popup()->set_item_icon(2, get_editor_theme_icon(SNAME("SnapGrid" ))); |
820 | button_pixel_snap->set_icon(button_pixel_snap->get_popup()->get_item_icon(current_snap_option)); |
821 | |
822 | PopupMenu *p = button_advanced_menu->get_popup(); |
823 | p->set_item_icon(p->get_item_index(ROTATE_RIGHT), get_editor_theme_icon(SNAME("RotateRight" ))); |
824 | p->set_item_icon(p->get_item_index(ROTATE_LEFT), get_editor_theme_icon(SNAME("RotateLeft" ))); |
825 | p->set_item_icon(p->get_item_index(FLIP_HORIZONTALLY), get_editor_theme_icon(SNAME("MirrorX" ))); |
826 | p->set_item_icon(p->get_item_index(FLIP_VERTICALLY), get_editor_theme_icon(SNAME("MirrorY" ))); |
827 | } break; |
828 | } |
829 | } |
830 | |
831 | void GenericTilePolygonEditor::_bind_methods() { |
832 | ClassDB::bind_method(D_METHOD("get_polygon_count" ), &GenericTilePolygonEditor::get_polygon_count); |
833 | ClassDB::bind_method(D_METHOD("add_polygon" , "polygon" , "index" ), &GenericTilePolygonEditor::add_polygon, DEFVAL(-1)); |
834 | ClassDB::bind_method(D_METHOD("remove_polygon" , "index" ), &GenericTilePolygonEditor::remove_polygon); |
835 | ClassDB::bind_method(D_METHOD("clear_polygons" ), &GenericTilePolygonEditor::clear_polygons); |
836 | ClassDB::bind_method(D_METHOD("set_polygon" , "index" , "polygon" ), &GenericTilePolygonEditor::set_polygon); |
837 | ClassDB::bind_method(D_METHOD("get_polygon" , "index" ), &GenericTilePolygonEditor::get_polygon); |
838 | |
839 | ADD_SIGNAL(MethodInfo("polygons_changed" )); |
840 | } |
841 | |
842 | GenericTilePolygonEditor::GenericTilePolygonEditor() { |
843 | toolbar = memnew(HBoxContainer); |
844 | add_child(toolbar); |
845 | |
846 | tools_button_group.instantiate(); |
847 | |
848 | button_expand = memnew(Button); |
849 | button_expand->set_flat(true); |
850 | button_expand->set_toggle_mode(true); |
851 | button_expand->set_pressed(false); |
852 | button_expand->set_tooltip_text(TTR("Expand editor" )); |
853 | button_expand->connect("toggled" , callable_mp(this, &GenericTilePolygonEditor::_toggle_expand)); |
854 | toolbar->add_child(button_expand); |
855 | |
856 | toolbar->add_child(memnew(VSeparator)); |
857 | |
858 | button_create = memnew(Button); |
859 | button_create->set_flat(true); |
860 | button_create->set_toggle_mode(true); |
861 | button_create->set_button_group(tools_button_group); |
862 | button_create->set_pressed(true); |
863 | button_create->set_tooltip_text(TTR("Add polygon tool" )); |
864 | toolbar->add_child(button_create); |
865 | |
866 | button_edit = memnew(Button); |
867 | button_edit->set_flat(true); |
868 | button_edit->set_toggle_mode(true); |
869 | button_edit->set_button_group(tools_button_group); |
870 | button_edit->set_tooltip_text(TTR("Edit points tool" )); |
871 | toolbar->add_child(button_edit); |
872 | |
873 | button_delete = memnew(Button); |
874 | button_delete->set_flat(true); |
875 | button_delete->set_toggle_mode(true); |
876 | button_delete->set_button_group(tools_button_group); |
877 | button_delete->set_tooltip_text(TTR("Delete points tool" )); |
878 | toolbar->add_child(button_delete); |
879 | |
880 | button_advanced_menu = memnew(MenuButton); |
881 | button_advanced_menu->set_flat(true); |
882 | button_advanced_menu->set_toggle_mode(true); |
883 | button_advanced_menu->get_popup()->add_item(TTR("Reset to default tile shape" ), RESET_TO_DEFAULT_TILE, Key::F); |
884 | button_advanced_menu->get_popup()->add_item(TTR("Clear" ), CLEAR_TILE, Key::C); |
885 | button_advanced_menu->get_popup()->add_separator(); |
886 | button_advanced_menu->get_popup()->add_item(TTR("Rotate Right" ), ROTATE_RIGHT, Key::R); |
887 | button_advanced_menu->get_popup()->add_item(TTR("Rotate Left" ), ROTATE_LEFT, Key::E); |
888 | button_advanced_menu->get_popup()->add_item(TTR("Flip Horizontally" ), FLIP_HORIZONTALLY, Key::H); |
889 | button_advanced_menu->get_popup()->add_item(TTR("Flip Vertically" ), FLIP_VERTICALLY, Key::V); |
890 | button_advanced_menu->get_popup()->connect("id_pressed" , callable_mp(this, &GenericTilePolygonEditor::_advanced_menu_item_pressed)); |
891 | button_advanced_menu->set_focus_mode(FOCUS_ALL); |
892 | toolbar->add_child(button_advanced_menu); |
893 | |
894 | toolbar->add_child(memnew(VSeparator)); |
895 | |
896 | button_pixel_snap = memnew(MenuButton); |
897 | toolbar->add_child(button_pixel_snap); |
898 | button_pixel_snap->set_flat(true); |
899 | button_pixel_snap->set_tooltip_text(TTR("Toggle Grid Snap" )); |
900 | button_pixel_snap->get_popup()->add_item(TTR("Disable Snap" ), SNAP_NONE); |
901 | button_pixel_snap->get_popup()->add_item(TTR("Half-Pixel Snap" ), SNAP_HALF_PIXEL); |
902 | button_pixel_snap->get_popup()->add_item(TTR("Grid Snap" ), SNAP_GRID); |
903 | button_pixel_snap->get_popup()->connect("index_pressed" , callable_mp(this, &GenericTilePolygonEditor::_set_snap_option)); |
904 | |
905 | snap_subdivision = memnew(SpinBox); |
906 | toolbar->add_child(snap_subdivision); |
907 | snap_subdivision->get_line_edit()->add_theme_constant_override("minimum_character_width" , 2); |
908 | snap_subdivision->set_min(1); |
909 | snap_subdivision->set_max(99); |
910 | |
911 | Control *root = memnew(Control); |
912 | root->set_v_size_flags(Control::SIZE_EXPAND_FILL); |
913 | root->set_custom_minimum_size(Size2(0, 200 * EDSCALE)); |
914 | root->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); |
915 | add_child(root); |
916 | |
917 | panel = memnew(Panel); |
918 | panel->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT); |
919 | panel->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); |
920 | root->add_child(panel); |
921 | |
922 | base_control = memnew(Control); |
923 | base_control->set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST); |
924 | base_control->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT); |
925 | base_control->connect("draw" , callable_mp(this, &GenericTilePolygonEditor::_base_control_draw)); |
926 | base_control->connect("gui_input" , callable_mp(this, &GenericTilePolygonEditor::_base_control_gui_input)); |
927 | base_control->set_clip_contents(true); |
928 | base_control->set_focus_mode(Control::FOCUS_CLICK); |
929 | root->add_child(base_control); |
930 | snap_subdivision->connect("value_changed" , callable_mp((CanvasItem *)base_control, &CanvasItem::queue_redraw).unbind(1)); |
931 | snap_subdivision->connect("value_changed" , callable_mp(this, &GenericTilePolygonEditor::_store_snap_options).unbind(1)); |
932 | |
933 | editor_zoom_widget = memnew(EditorZoomWidget); |
934 | editor_zoom_widget->set_position(Vector2(5, 5)); |
935 | editor_zoom_widget->connect("zoom_changed" , callable_mp(this, &GenericTilePolygonEditor::_zoom_changed).unbind(1)); |
936 | editor_zoom_widget->set_shortcut_context(this); |
937 | root->add_child(editor_zoom_widget); |
938 | |
939 | button_center_view = memnew(Button); |
940 | button_center_view->set_anchors_and_offsets_preset(Control::PRESET_TOP_RIGHT, Control::PRESET_MODE_MINSIZE, 5); |
941 | button_center_view->connect("pressed" , callable_mp(this, &GenericTilePolygonEditor::_center_view)); |
942 | button_center_view->set_flat(true); |
943 | button_center_view->set_disabled(true); |
944 | root->add_child(button_center_view); |
945 | |
946 | snap_subdivision->set_value_no_signal(EditorSettings::get_singleton()->get_project_metadata("editor_metadata" , "tile_snap_subdiv" , 4)); |
947 | _set_snap_option(EditorSettings::get_singleton()->get_project_metadata("editor_metadata" , "tile_snap_option" , SNAP_NONE)); |
948 | } |
949 | |
950 | void TileDataDefaultEditor::_property_value_changed(StringName p_property, Variant p_value, StringName p_field) { |
951 | ERR_FAIL_NULL(dummy_object); |
952 | dummy_object->set(p_property, p_value); |
953 | emit_signal(SNAME("needs_redraw" )); |
954 | } |
955 | |
956 | Variant TileDataDefaultEditor::_get_painted_value() { |
957 | ERR_FAIL_NULL_V(dummy_object, Variant()); |
958 | return dummy_object->get(property); |
959 | } |
960 | |
961 | void TileDataDefaultEditor::_set_painted_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) { |
962 | TileData *tile_data = p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile); |
963 | ERR_FAIL_NULL(tile_data); |
964 | Variant value = tile_data->get(property); |
965 | dummy_object->set(property, value); |
966 | if (property_editor) { |
967 | property_editor->update_property(); |
968 | } |
969 | } |
970 | |
971 | void TileDataDefaultEditor::_set_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile, Variant p_value) { |
972 | TileData *tile_data = p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile); |
973 | ERR_FAIL_NULL(tile_data); |
974 | tile_data->set(property, p_value); |
975 | } |
976 | |
977 | Variant TileDataDefaultEditor::_get_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) { |
978 | TileData *tile_data = p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile); |
979 | ERR_FAIL_NULL_V(tile_data, Variant()); |
980 | return tile_data->get(property); |
981 | } |
982 | |
983 | void TileDataDefaultEditor::_setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, HashMap<TileMapCell, Variant, TileMapCell> p_previous_values, Variant p_new_value) { |
984 | EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); |
985 | for (const KeyValue<TileMapCell, Variant> &E : p_previous_values) { |
986 | Vector2i coords = E.key.get_atlas_coords(); |
987 | undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/%s" , coords.x, coords.y, E.key.alternative_tile, property), E.value); |
988 | undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/%s" , coords.x, coords.y, E.key.alternative_tile, property), p_new_value); |
989 | } |
990 | } |
991 | |
992 | void TileDataDefaultEditor::forward_draw_over_atlas(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_set_atlas_source, CanvasItem *p_canvas_item, Transform2D p_transform) { |
993 | if (drag_type == DRAG_TYPE_PAINT_RECT) { |
994 | Color grid_color = EDITOR_GET("editors/tiles_editor/grid_color" ); |
995 | 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); |
996 | |
997 | p_canvas_item->draw_set_transform_matrix(p_transform); |
998 | |
999 | Rect2i rect; |
1000 | rect.set_position(p_tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_pos, true)); |
1001 | rect.set_end(p_tile_atlas_view->get_atlas_tile_coords_at_pos(p_transform.affine_inverse().xform(p_canvas_item->get_local_mouse_position()), true)); |
1002 | rect = rect.abs(); |
1003 | |
1004 | RBSet<TileMapCell> edited; |
1005 | for (int x = rect.get_position().x; x <= rect.get_end().x; x++) { |
1006 | for (int y = rect.get_position().y; y <= rect.get_end().y; y++) { |
1007 | Vector2i coords = Vector2i(x, y); |
1008 | coords = p_tile_set_atlas_source->get_tile_at_coords(coords); |
1009 | if (coords != TileSetSource::INVALID_ATLAS_COORDS) { |
1010 | TileMapCell cell; |
1011 | cell.source_id = 0; |
1012 | cell.set_atlas_coords(coords); |
1013 | cell.alternative_tile = 0; |
1014 | edited.insert(cell); |
1015 | } |
1016 | } |
1017 | } |
1018 | |
1019 | for (const TileMapCell &E : edited) { |
1020 | Vector2i coords = E.get_atlas_coords(); |
1021 | p_canvas_item->draw_rect(p_tile_set_atlas_source->get_tile_texture_region(coords), selection_color, false); |
1022 | } |
1023 | p_canvas_item->draw_set_transform_matrix(Transform2D()); |
1024 | } |
1025 | }; |
1026 | |
1027 | void TileDataDefaultEditor::forward_draw_over_alternatives(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_set_atlas_source, CanvasItem *p_canvas_item, Transform2D p_transform){ |
1028 | |
1029 | }; |
1030 | |
1031 | void TileDataDefaultEditor::forward_painting_atlas_gui_input(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_set_atlas_source, const Ref<InputEvent> &p_event) { |
1032 | Ref<InputEventMouseMotion> mm = p_event; |
1033 | if (mm.is_valid()) { |
1034 | if (drag_type == DRAG_TYPE_PAINT) { |
1035 | Vector<Vector2i> line = Geometry2D::bresenham_line(p_tile_atlas_view->get_atlas_tile_coords_at_pos(drag_last_pos, true), p_tile_atlas_view->get_atlas_tile_coords_at_pos(mm->get_position(), true)); |
1036 | for (int i = 0; i < line.size(); i++) { |
1037 | Vector2i coords = p_tile_set_atlas_source->get_tile_at_coords(line[i]); |
1038 | if (coords != TileSetSource::INVALID_ATLAS_COORDS) { |
1039 | TileMapCell cell; |
1040 | cell.source_id = 0; |
1041 | cell.set_atlas_coords(coords); |
1042 | cell.alternative_tile = 0; |
1043 | if (!drag_modified.has(cell)) { |
1044 | drag_modified[cell] = _get_value(p_tile_set_atlas_source, coords, 0); |
1045 | } |
1046 | _set_value(p_tile_set_atlas_source, coords, 0, drag_painted_value); |
1047 | } |
1048 | } |
1049 | drag_last_pos = mm->get_position(); |
1050 | } |
1051 | } |
1052 | |
1053 | EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); |
1054 | Ref<InputEventMouseButton> mb = p_event; |
1055 | if (mb.is_valid()) { |
1056 | if (mb->get_button_index() == MouseButton::LEFT) { |
1057 | if (mb->is_pressed()) { |
1058 | if (picker_button->is_pressed() || (mb->is_command_or_control_pressed() && !mb->is_shift_pressed())) { |
1059 | Vector2i coords = p_tile_atlas_view->get_atlas_tile_coords_at_pos(mb->get_position(), true); |
1060 | coords = p_tile_set_atlas_source->get_tile_at_coords(coords); |
1061 | if (coords != TileSetSource::INVALID_ATLAS_COORDS) { |
1062 | _set_painted_value(p_tile_set_atlas_source, coords, 0); |
1063 | picker_button->set_pressed(false); |
1064 | } |
1065 | } else if (mb->is_command_or_control_pressed() && mb->is_shift_pressed()) { |
1066 | drag_type = DRAG_TYPE_PAINT_RECT; |
1067 | drag_modified.clear(); |
1068 | drag_painted_value = _get_painted_value(); |
1069 | drag_start_pos = mb->get_position(); |
1070 | } else { |
1071 | drag_type = DRAG_TYPE_PAINT; |
1072 | drag_modified.clear(); |
1073 | drag_painted_value = _get_painted_value(); |
1074 | Vector2i coords = p_tile_atlas_view->get_atlas_tile_coords_at_pos(mb->get_position(), true); |
1075 | coords = p_tile_set_atlas_source->get_tile_at_coords(coords); |
1076 | if (coords != TileSetSource::INVALID_ATLAS_COORDS) { |
1077 | TileMapCell cell; |
1078 | cell.source_id = 0; |
1079 | cell.set_atlas_coords(coords); |
1080 | cell.alternative_tile = 0; |
1081 | drag_modified[cell] = _get_value(p_tile_set_atlas_source, coords, 0); |
1082 | _set_value(p_tile_set_atlas_source, coords, 0, drag_painted_value); |
1083 | } |
1084 | drag_last_pos = mb->get_position(); |
1085 | } |
1086 | } else { |
1087 | if (drag_type == DRAG_TYPE_PAINT_RECT) { |
1088 | Rect2i rect; |
1089 | rect.set_position(p_tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_pos, true)); |
1090 | rect.set_end(p_tile_atlas_view->get_atlas_tile_coords_at_pos(mb->get_position(), true)); |
1091 | rect = rect.abs(); |
1092 | |
1093 | drag_modified.clear(); |
1094 | for (int x = rect.get_position().x; x <= rect.get_end().x; x++) { |
1095 | for (int y = rect.get_position().y; y <= rect.get_end().y; y++) { |
1096 | Vector2i coords = Vector2i(x, y); |
1097 | coords = p_tile_set_atlas_source->get_tile_at_coords(coords); |
1098 | if (coords != TileSetSource::INVALID_ATLAS_COORDS) { |
1099 | TileMapCell cell; |
1100 | cell.source_id = 0; |
1101 | cell.set_atlas_coords(coords); |
1102 | cell.alternative_tile = 0; |
1103 | drag_modified[cell] = _get_value(p_tile_set_atlas_source, coords, 0); |
1104 | } |
1105 | } |
1106 | } |
1107 | undo_redo->create_action(TTR("Painting Tiles Property" )); |
1108 | _setup_undo_redo_action(p_tile_set_atlas_source, drag_modified, drag_painted_value); |
1109 | undo_redo->commit_action(true); |
1110 | drag_type = DRAG_TYPE_NONE; |
1111 | } else if (drag_type == DRAG_TYPE_PAINT) { |
1112 | undo_redo->create_action(TTR("Painting Tiles Property" )); |
1113 | _setup_undo_redo_action(p_tile_set_atlas_source, drag_modified, drag_painted_value); |
1114 | undo_redo->commit_action(false); |
1115 | drag_type = DRAG_TYPE_NONE; |
1116 | } |
1117 | } |
1118 | } |
1119 | } |
1120 | } |
1121 | |
1122 | void TileDataDefaultEditor::forward_painting_alternatives_gui_input(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_set_atlas_source, const Ref<InputEvent> &p_event) { |
1123 | Ref<InputEventMouseMotion> mm = p_event; |
1124 | if (mm.is_valid()) { |
1125 | if (drag_type == DRAG_TYPE_PAINT) { |
1126 | Vector3i tile = p_tile_atlas_view->get_alternative_tile_at_pos(mm->get_position()); |
1127 | Vector2i coords = Vector2i(tile.x, tile.y); |
1128 | int alternative_tile = tile.z; |
1129 | |
1130 | if (coords != TileSetSource::INVALID_ATLAS_COORDS) { |
1131 | TileMapCell cell; |
1132 | cell.source_id = 0; |
1133 | cell.set_atlas_coords(coords); |
1134 | cell.alternative_tile = alternative_tile; |
1135 | if (!drag_modified.has(cell)) { |
1136 | drag_modified[cell] = _get_value(p_tile_set_atlas_source, coords, alternative_tile); |
1137 | } |
1138 | _set_value(p_tile_set_atlas_source, coords, alternative_tile, drag_painted_value); |
1139 | } |
1140 | |
1141 | drag_last_pos = mm->get_position(); |
1142 | } |
1143 | } |
1144 | |
1145 | Ref<InputEventMouseButton> mb = p_event; |
1146 | if (mb.is_valid()) { |
1147 | if (mb->get_button_index() == MouseButton::LEFT) { |
1148 | if (mb->is_pressed()) { |
1149 | if (picker_button->is_pressed()) { |
1150 | Vector3i tile = p_tile_atlas_view->get_alternative_tile_at_pos(mb->get_position()); |
1151 | Vector2i coords = Vector2i(tile.x, tile.y); |
1152 | int alternative_tile = tile.z; |
1153 | if (coords != TileSetSource::INVALID_ATLAS_COORDS) { |
1154 | _set_painted_value(p_tile_set_atlas_source, coords, alternative_tile); |
1155 | picker_button->set_pressed(false); |
1156 | } |
1157 | } else { |
1158 | drag_type = DRAG_TYPE_PAINT; |
1159 | drag_modified.clear(); |
1160 | drag_painted_value = _get_painted_value(); |
1161 | |
1162 | Vector3i tile = p_tile_atlas_view->get_alternative_tile_at_pos(mb->get_position()); |
1163 | Vector2i coords = Vector2i(tile.x, tile.y); |
1164 | int alternative_tile = tile.z; |
1165 | |
1166 | if (coords != TileSetSource::INVALID_ATLAS_COORDS) { |
1167 | TileMapCell cell; |
1168 | cell.source_id = 0; |
1169 | cell.set_atlas_coords(coords); |
1170 | cell.alternative_tile = alternative_tile; |
1171 | drag_modified[cell] = _get_value(p_tile_set_atlas_source, coords, alternative_tile); |
1172 | _set_value(p_tile_set_atlas_source, coords, alternative_tile, drag_painted_value); |
1173 | } |
1174 | drag_last_pos = mb->get_position(); |
1175 | } |
1176 | } else { |
1177 | EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); |
1178 | undo_redo->create_action(TTR("Painting Tiles Property" )); |
1179 | _setup_undo_redo_action(p_tile_set_atlas_source, drag_modified, drag_painted_value); |
1180 | undo_redo->commit_action(false); |
1181 | drag_type = DRAG_TYPE_NONE; |
1182 | } |
1183 | } |
1184 | } |
1185 | } |
1186 | |
1187 | void TileDataDefaultEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected) { |
1188 | TileData *tile_data = _get_tile_data(p_cell); |
1189 | ERR_FAIL_NULL(tile_data); |
1190 | |
1191 | bool valid; |
1192 | Variant value = tile_data->get(property, &valid); |
1193 | if (!valid) { |
1194 | return; |
1195 | } |
1196 | |
1197 | Vector2 texture_origin = tile_data->get_texture_origin(); |
1198 | if (value.get_type() == Variant::BOOL) { |
1199 | Ref<Texture2D> texture = (bool)value ? tile_bool_checked : tile_bool_unchecked; |
1200 | int size = MIN(tile_set->get_tile_size().x, tile_set->get_tile_size().y) / 3; |
1201 | Rect2 rect = p_transform.xform(Rect2(Vector2(-size / 2, -size / 2) - texture_origin, Vector2(size, size))); |
1202 | p_canvas_item->draw_texture_rect(texture, rect); |
1203 | } else if (value.get_type() == Variant::COLOR) { |
1204 | int size = MIN(tile_set->get_tile_size().x, tile_set->get_tile_size().y) / 3; |
1205 | Rect2 rect = p_transform.xform(Rect2(Vector2(-size / 2, -size / 2) - texture_origin, Vector2(size, size))); |
1206 | p_canvas_item->draw_rect(rect, value); |
1207 | } else { |
1208 | Ref<Font> font = TileSetEditor::get_singleton()->get_theme_font(SNAME("bold" ), EditorStringName(EditorFonts)); |
1209 | int font_size = TileSetEditor::get_singleton()->get_theme_font_size(SNAME("bold_size" ), EditorStringName(EditorFonts)); |
1210 | String text; |
1211 | // Round floating point precision to 2 digits, as tiles don't have that much space. |
1212 | switch (value.get_type()) { |
1213 | case Variant::FLOAT: |
1214 | text = vformat("%.2f" , value); |
1215 | break; |
1216 | case Variant::VECTOR2: |
1217 | case Variant::VECTOR3: |
1218 | case Variant::VECTOR4: |
1219 | text = vformat("%.2v" , value); |
1220 | break; |
1221 | default: |
1222 | text = value.stringify(); |
1223 | break; |
1224 | } |
1225 | |
1226 | Color color = Color(1, 1, 1); |
1227 | if (p_selected) { |
1228 | Color grid_color = EDITOR_GET("editors/tiles_editor/grid_color" ); |
1229 | 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); |
1230 | selection_color.set_v(0.9); |
1231 | color = selection_color; |
1232 | } else if (is_visible_in_tree()) { |
1233 | Variant painted_value = _get_painted_value(); |
1234 | bool equal = (painted_value.get_type() == Variant::FLOAT && value.get_type() == Variant::FLOAT) ? Math::is_equal_approx(float(painted_value), float(value)) : painted_value == value; |
1235 | if (equal) { |
1236 | color = Color(0.7, 0.7, 0.7); |
1237 | } |
1238 | } |
1239 | |
1240 | Vector2 string_size = font->get_string_size(text, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size); |
1241 | p_canvas_item->draw_string_outline(font, p_transform.xform(-texture_origin) + Vector2i(-string_size.x / 2, string_size.y / 4), text, HORIZONTAL_ALIGNMENT_CENTER, string_size.x, font_size, 3, Color(0, 0, 0)); |
1242 | p_canvas_item->draw_string(font, p_transform.xform(-texture_origin) + Vector2i(-string_size.x / 2, string_size.y / 4), text, HORIZONTAL_ALIGNMENT_CENTER, string_size.x, font_size, color); |
1243 | } |
1244 | } |
1245 | |
1246 | void TileDataDefaultEditor::setup_property_editor(Variant::Type p_type, String p_property, String p_label, Variant p_default_value) { |
1247 | ERR_FAIL_COND_MSG(!property.is_empty(), "Cannot setup TileDataDefaultEditor twice" ); |
1248 | property = p_property; |
1249 | property_type = p_type; |
1250 | |
1251 | // Update everything. |
1252 | if (property_editor) { |
1253 | property_editor->queue_free(); |
1254 | } |
1255 | |
1256 | // Update the dummy object. |
1257 | dummy_object->add_dummy_property(p_property); |
1258 | |
1259 | // Get the default value for the type. |
1260 | if (p_default_value == Variant()) { |
1261 | Callable::CallError error; |
1262 | Variant painted_value; |
1263 | Variant::construct(p_type, painted_value, nullptr, 0, error); |
1264 | dummy_object->set(p_property, painted_value); |
1265 | } else { |
1266 | dummy_object->set(p_property, p_default_value); |
1267 | } |
1268 | |
1269 | // Create and setup the property editor. |
1270 | property_editor = EditorInspectorDefaultPlugin::get_editor_for_property(dummy_object, p_type, p_property, PROPERTY_HINT_NONE, "" , PROPERTY_USAGE_DEFAULT); |
1271 | property_editor->set_object_and_property(dummy_object, p_property); |
1272 | if (p_label.is_empty()) { |
1273 | property_editor->set_label(EditorPropertyNameProcessor::get_singleton()->process_name(p_property, EditorPropertyNameProcessor::get_default_inspector_style())); |
1274 | } else { |
1275 | property_editor->set_label(p_label); |
1276 | } |
1277 | property_editor->connect("property_changed" , callable_mp(this, &TileDataDefaultEditor::_property_value_changed).unbind(1)); |
1278 | property_editor->set_tooltip_text(p_property); |
1279 | property_editor->update_property(); |
1280 | add_child(property_editor); |
1281 | } |
1282 | |
1283 | void TileDataDefaultEditor::_notification(int p_what) { |
1284 | switch (p_what) { |
1285 | case NOTIFICATION_ENTER_TREE: |
1286 | case NOTIFICATION_THEME_CHANGED: { |
1287 | picker_button->set_icon(get_editor_theme_icon(SNAME("ColorPick" ))); |
1288 | tile_bool_checked = get_editor_theme_icon(SNAME("TileChecked" )); |
1289 | tile_bool_unchecked = get_editor_theme_icon(SNAME("TileUnchecked" )); |
1290 | } break; |
1291 | } |
1292 | } |
1293 | |
1294 | Variant::Type TileDataDefaultEditor::get_property_type() { |
1295 | return property_type; |
1296 | } |
1297 | |
1298 | TileDataDefaultEditor::TileDataDefaultEditor() { |
1299 | label = memnew(Label); |
1300 | label->set_text(TTR("Painting:" )); |
1301 | label->set_theme_type_variation("HeaderSmall" ); |
1302 | add_child(label); |
1303 | |
1304 | picker_button = memnew(Button); |
1305 | picker_button->set_flat(true); |
1306 | picker_button->set_toggle_mode(true); |
1307 | picker_button->set_shortcut(ED_SHORTCUT("tiles_editor/picker" , TTR("Picker" ), Key::P)); |
1308 | toolbar->add_child(picker_button); |
1309 | } |
1310 | |
1311 | TileDataDefaultEditor::~TileDataDefaultEditor() { |
1312 | toolbar->queue_free(); |
1313 | memdelete(dummy_object); |
1314 | } |
1315 | |
1316 | void TileDataTextureOriginEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected) { |
1317 | TileData *tile_data = _get_tile_data(p_cell); |
1318 | ERR_FAIL_NULL(tile_data); |
1319 | |
1320 | Vector2i tile_set_tile_size = tile_set->get_tile_size(); |
1321 | Color color = Color(1.0, 1.0, 1.0); |
1322 | if (p_selected) { |
1323 | Color grid_color = EDITOR_GET("editors/tiles_editor/grid_color" ); |
1324 | 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); |
1325 | color = selection_color; |
1326 | } |
1327 | |
1328 | TileSetSource *source = *(tile_set->get_source(p_cell.source_id)); |
1329 | TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); |
1330 | if (atlas_source->is_position_in_tile_texture_region(p_cell.get_atlas_coords(), p_cell.alternative_tile, -tile_set_tile_size / 2) && atlas_source->is_position_in_tile_texture_region(p_cell.get_atlas_coords(), p_cell.alternative_tile, tile_set_tile_size / 2 - Vector2(1, 1))) { |
1331 | Transform2D tile_xform; |
1332 | tile_xform.set_scale(tile_set_tile_size); |
1333 | tile_set->draw_tile_shape(p_canvas_item, p_transform * tile_xform, color); |
1334 | } |
1335 | |
1336 | if (atlas_source->is_position_in_tile_texture_region(p_cell.get_atlas_coords(), p_cell.alternative_tile, Vector2())) { |
1337 | Ref<Texture2D> position_icon = TileSetEditor::get_singleton()->get_editor_theme_icon(SNAME("EditorPosition" )); |
1338 | p_canvas_item->draw_texture(position_icon, p_transform.xform(Vector2()) - (position_icon->get_size() / 2), color); |
1339 | } else { |
1340 | Ref<Font> font = TileSetEditor::get_singleton()->get_theme_font(SNAME("bold" ), EditorStringName(EditorFonts)); |
1341 | int font_size = TileSetEditor::get_singleton()->get_theme_font_size(SNAME("bold_size" ), EditorStringName(EditorFonts)); |
1342 | Vector2 texture_origin = tile_data->get_texture_origin(); |
1343 | String text = vformat("%s" , texture_origin); |
1344 | Vector2 string_size = font->get_string_size(text, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size); |
1345 | p_canvas_item->draw_string_outline(font, p_transform.xform(-texture_origin) + Vector2i(-string_size.x / 2, string_size.y / 2), text, HORIZONTAL_ALIGNMENT_CENTER, string_size.x, font_size, 1, Color(0, 0, 0, 1)); |
1346 | p_canvas_item->draw_string(font, p_transform.xform(-texture_origin) + Vector2i(-string_size.x / 2, string_size.y / 2), text, HORIZONTAL_ALIGNMENT_CENTER, string_size.x, font_size, color); |
1347 | } |
1348 | } |
1349 | |
1350 | void TileDataPositionEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected) { |
1351 | TileData *tile_data = _get_tile_data(p_cell); |
1352 | ERR_FAIL_NULL(tile_data); |
1353 | |
1354 | bool valid; |
1355 | Variant value = tile_data->get(property, &valid); |
1356 | if (!valid) { |
1357 | return; |
1358 | } |
1359 | ERR_FAIL_COND(value.get_type() != Variant::VECTOR2I && value.get_type() != Variant::VECTOR2); |
1360 | |
1361 | Color color = Color(1.0, 1.0, 1.0); |
1362 | if (p_selected) { |
1363 | Color grid_color = EDITOR_GET("editors/tiles_editor/grid_color" ); |
1364 | 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); |
1365 | color = selection_color; |
1366 | } |
1367 | Ref<Texture2D> position_icon = TileSetEditor::get_singleton()->get_editor_theme_icon(SNAME("EditorPosition" )); |
1368 | p_canvas_item->draw_texture(position_icon, p_transform.xform(Vector2(value)) - position_icon->get_size() / 2, color); |
1369 | } |
1370 | |
1371 | void TileDataYSortEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected) { |
1372 | TileData *tile_data = _get_tile_data(p_cell); |
1373 | ERR_FAIL_NULL(tile_data); |
1374 | |
1375 | Color color = Color(1.0, 1.0, 1.0); |
1376 | if (p_selected) { |
1377 | Color grid_color = EDITOR_GET("editors/tiles_editor/grid_color" ); |
1378 | 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); |
1379 | color = selection_color; |
1380 | } |
1381 | Vector2 texture_origin = tile_data->get_texture_origin(); |
1382 | TileSetSource *source = *(tile_set->get_source(p_cell.source_id)); |
1383 | TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); |
1384 | if (atlas_source->is_position_in_tile_texture_region(p_cell.get_atlas_coords(), p_cell.alternative_tile, Vector2(0, tile_data->get_y_sort_origin()))) { |
1385 | Ref<Texture2D> position_icon = TileSetEditor::get_singleton()->get_editor_theme_icon(SNAME("EditorPosition" )); |
1386 | p_canvas_item->draw_texture(position_icon, p_transform.xform(Vector2(0, tile_data->get_y_sort_origin())) - position_icon->get_size() / 2, color); |
1387 | } else { |
1388 | Ref<Font> font = TileSetEditor::get_singleton()->get_theme_font(SNAME("bold" ), EditorStringName(EditorFonts)); |
1389 | int font_size = TileSetEditor::get_singleton()->get_theme_font_size(SNAME("bold_size" ), EditorStringName(EditorFonts)); |
1390 | String text = vformat("%s" , tile_data->get_y_sort_origin()); |
1391 | |
1392 | Vector2 string_size = font->get_string_size(text, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size); |
1393 | p_canvas_item->draw_string_outline(font, p_transform.xform(-texture_origin) + Vector2i(-string_size.x / 2, string_size.y / 2), text, HORIZONTAL_ALIGNMENT_CENTER, string_size.x, font_size, 1, Color(0, 0, 0, 1)); |
1394 | p_canvas_item->draw_string(font, p_transform.xform(-texture_origin) + Vector2i(-string_size.x / 2, string_size.y / 2), text, HORIZONTAL_ALIGNMENT_CENTER, string_size.x, font_size, color); |
1395 | } |
1396 | } |
1397 | |
1398 | void TileDataOcclusionShapeEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected) { |
1399 | TileData *tile_data = _get_tile_data(p_cell); |
1400 | ERR_FAIL_NULL(tile_data); |
1401 | |
1402 | Color grid_color = EDITOR_GET("editors/tiles_editor/grid_color" ); |
1403 | 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); |
1404 | Color color = grid_color.darkened(0.2); |
1405 | if (p_selected) { |
1406 | color = selection_color.darkened(0.2); |
1407 | } |
1408 | color.a *= 0.5; |
1409 | |
1410 | Vector<Color> debug_occlusion_color; |
1411 | debug_occlusion_color.push_back(color); |
1412 | |
1413 | RenderingServer::get_singleton()->canvas_item_add_set_transform(p_canvas_item->get_canvas_item(), p_transform); |
1414 | Ref<OccluderPolygon2D> occluder = tile_data->get_occluder(occlusion_layer); |
1415 | if (occluder.is_valid() && occluder->get_polygon().size() >= 3) { |
1416 | p_canvas_item->draw_polygon(Variant(occluder->get_polygon()), debug_occlusion_color); |
1417 | } |
1418 | RenderingServer::get_singleton()->canvas_item_add_set_transform(p_canvas_item->get_canvas_item(), Transform2D()); |
1419 | } |
1420 | |
1421 | Variant TileDataOcclusionShapeEditor::_get_painted_value() { |
1422 | Ref<OccluderPolygon2D> occluder_polygon; |
1423 | occluder_polygon.instantiate(); |
1424 | if (polygon_editor->get_polygon_count() >= 1) { |
1425 | occluder_polygon->set_polygon(polygon_editor->get_polygon(0)); |
1426 | } |
1427 | return occluder_polygon; |
1428 | } |
1429 | |
1430 | void TileDataOcclusionShapeEditor::_set_painted_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) { |
1431 | TileData *tile_data = p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile); |
1432 | ERR_FAIL_NULL(tile_data); |
1433 | |
1434 | Ref<OccluderPolygon2D> occluder_polygon = tile_data->get_occluder(occlusion_layer); |
1435 | polygon_editor->clear_polygons(); |
1436 | if (occluder_polygon.is_valid()) { |
1437 | polygon_editor->add_polygon(occluder_polygon->get_polygon()); |
1438 | } |
1439 | polygon_editor->set_background(p_tile_set_atlas_source->get_texture(), p_tile_set_atlas_source->get_tile_texture_region(p_coords), tile_data->get_texture_origin(), tile_data->get_flip_h(), tile_data->get_flip_v(), tile_data->get_transpose(), tile_data->get_modulate()); |
1440 | } |
1441 | |
1442 | void TileDataOcclusionShapeEditor::_set_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile, Variant p_value) { |
1443 | TileData *tile_data = p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile); |
1444 | ERR_FAIL_NULL(tile_data); |
1445 | Ref<OccluderPolygon2D> occluder_polygon = p_value; |
1446 | tile_data->set_occluder(occlusion_layer, occluder_polygon); |
1447 | |
1448 | polygon_editor->set_background(p_tile_set_atlas_source->get_texture(), p_tile_set_atlas_source->get_tile_texture_region(p_coords), tile_data->get_texture_origin(), tile_data->get_flip_h(), tile_data->get_flip_v(), tile_data->get_transpose(), tile_data->get_modulate()); |
1449 | } |
1450 | |
1451 | Variant TileDataOcclusionShapeEditor::_get_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) { |
1452 | TileData *tile_data = p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile); |
1453 | ERR_FAIL_NULL_V(tile_data, Variant()); |
1454 | return tile_data->get_occluder(occlusion_layer); |
1455 | } |
1456 | |
1457 | void TileDataOcclusionShapeEditor::_setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, HashMap<TileMapCell, Variant, TileMapCell> p_previous_values, Variant p_new_value) { |
1458 | EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); |
1459 | for (const KeyValue<TileMapCell, Variant> &E : p_previous_values) { |
1460 | Vector2i coords = E.key.get_atlas_coords(); |
1461 | undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/occlusion_layer_%d/polygon" , coords.x, coords.y, E.key.alternative_tile, occlusion_layer), E.value); |
1462 | undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/occlusion_layer_%d/polygon" , coords.x, coords.y, E.key.alternative_tile, occlusion_layer), p_new_value); |
1463 | } |
1464 | } |
1465 | |
1466 | void TileDataOcclusionShapeEditor::_tile_set_changed() { |
1467 | polygon_editor->set_tile_set(tile_set); |
1468 | } |
1469 | |
1470 | void TileDataOcclusionShapeEditor::_notification(int p_what) { |
1471 | switch (p_what) { |
1472 | case NOTIFICATION_ENTER_TREE: { |
1473 | polygon_editor->set_polygons_color(get_tree()->get_debug_collisions_color()); |
1474 | } break; |
1475 | } |
1476 | } |
1477 | |
1478 | TileDataOcclusionShapeEditor::TileDataOcclusionShapeEditor() { |
1479 | polygon_editor = memnew(GenericTilePolygonEditor); |
1480 | add_child(polygon_editor); |
1481 | } |
1482 | |
1483 | void TileDataCollisionEditor::_property_value_changed(StringName p_property, Variant p_value, StringName p_field) { |
1484 | dummy_object->set(p_property, p_value); |
1485 | } |
1486 | |
1487 | void TileDataCollisionEditor::_property_selected(StringName p_path, int p_focusable) { |
1488 | // Deselect all other properties |
1489 | for (KeyValue<StringName, EditorProperty *> &editor : property_editors) { |
1490 | if (editor.key != p_path) { |
1491 | editor.value->deselect(); |
1492 | } |
1493 | } |
1494 | } |
1495 | |
1496 | void TileDataCollisionEditor::_polygons_changed() { |
1497 | // Update the dummy object properties and their editors. |
1498 | for (int i = 0; i < polygon_editor->get_polygon_count(); i++) { |
1499 | StringName one_way_property = vformat("polygon_%d_one_way" , i); |
1500 | StringName one_way_margin_property = vformat("polygon_%d_one_way_margin" , i); |
1501 | |
1502 | if (!dummy_object->has_dummy_property(one_way_property)) { |
1503 | dummy_object->add_dummy_property(one_way_property); |
1504 | dummy_object->set(one_way_property, false); |
1505 | } |
1506 | |
1507 | if (!dummy_object->has_dummy_property(one_way_margin_property)) { |
1508 | dummy_object->add_dummy_property(one_way_margin_property); |
1509 | dummy_object->set(one_way_margin_property, 1.0); |
1510 | } |
1511 | |
1512 | if (!property_editors.has(one_way_property)) { |
1513 | EditorProperty *one_way_property_editor = EditorInspectorDefaultPlugin::get_editor_for_property(dummy_object, Variant::BOOL, one_way_property, PROPERTY_HINT_NONE, "" , PROPERTY_USAGE_DEFAULT); |
1514 | one_way_property_editor->set_object_and_property(dummy_object, one_way_property); |
1515 | one_way_property_editor->set_label(one_way_property); |
1516 | one_way_property_editor->connect("property_changed" , callable_mp(this, &TileDataCollisionEditor::_property_value_changed).unbind(1)); |
1517 | one_way_property_editor->connect("selected" , callable_mp(this, &TileDataCollisionEditor::_property_selected)); |
1518 | one_way_property_editor->set_tooltip_text(one_way_property_editor->get_edited_property()); |
1519 | one_way_property_editor->update_property(); |
1520 | add_child(one_way_property_editor); |
1521 | property_editors[one_way_property] = one_way_property_editor; |
1522 | } |
1523 | |
1524 | if (!property_editors.has(one_way_margin_property)) { |
1525 | EditorProperty *one_way_margin_property_editor = EditorInspectorDefaultPlugin::get_editor_for_property(dummy_object, Variant::FLOAT, one_way_margin_property, PROPERTY_HINT_NONE, "" , PROPERTY_USAGE_DEFAULT); |
1526 | one_way_margin_property_editor->set_object_and_property(dummy_object, one_way_margin_property); |
1527 | one_way_margin_property_editor->set_label(one_way_margin_property); |
1528 | one_way_margin_property_editor->connect("property_changed" , callable_mp(this, &TileDataCollisionEditor::_property_value_changed).unbind(1)); |
1529 | one_way_margin_property_editor->connect("selected" , callable_mp(this, &TileDataCollisionEditor::_property_selected)); |
1530 | one_way_margin_property_editor->set_tooltip_text(one_way_margin_property_editor->get_edited_property()); |
1531 | one_way_margin_property_editor->update_property(); |
1532 | add_child(one_way_margin_property_editor); |
1533 | property_editors[one_way_margin_property] = one_way_margin_property_editor; |
1534 | } |
1535 | } |
1536 | |
1537 | // Remove unneeded properties and their editors. |
1538 | for (int i = polygon_editor->get_polygon_count(); dummy_object->has_dummy_property(vformat("polygon_%d_one_way" , i)); i++) { |
1539 | dummy_object->remove_dummy_property(vformat("polygon_%d_one_way" , i)); |
1540 | } |
1541 | for (int i = polygon_editor->get_polygon_count(); dummy_object->has_dummy_property(vformat("polygon_%d_one_way_margin" , i)); i++) { |
1542 | dummy_object->remove_dummy_property(vformat("polygon_%d_one_way_margin" , i)); |
1543 | } |
1544 | for (int i = polygon_editor->get_polygon_count(); property_editors.has(vformat("polygon_%d_one_way" , i)); i++) { |
1545 | property_editors[vformat("polygon_%d_one_way" , i)]->queue_free(); |
1546 | property_editors.erase(vformat("polygon_%d_one_way" , i)); |
1547 | } |
1548 | for (int i = polygon_editor->get_polygon_count(); property_editors.has(vformat("polygon_%d_one_way_margin" , i)); i++) { |
1549 | property_editors[vformat("polygon_%d_one_way_margin" , i)]->queue_free(); |
1550 | property_editors.erase(vformat("polygon_%d_one_way_margin" , i)); |
1551 | } |
1552 | } |
1553 | |
1554 | Variant TileDataCollisionEditor::_get_painted_value() { |
1555 | Dictionary dict; |
1556 | dict["linear_velocity" ] = dummy_object->get("linear_velocity" ); |
1557 | dict["angular_velocity" ] = dummy_object->get("angular_velocity" ); |
1558 | Array array; |
1559 | for (int i = 0; i < polygon_editor->get_polygon_count(); i++) { |
1560 | ERR_FAIL_COND_V(polygon_editor->get_polygon(i).size() < 3, Variant()); |
1561 | Dictionary polygon_dict; |
1562 | polygon_dict["points" ] = polygon_editor->get_polygon(i); |
1563 | polygon_dict["one_way" ] = dummy_object->get(vformat("polygon_%d_one_way" , i)); |
1564 | polygon_dict["one_way_margin" ] = dummy_object->get(vformat("polygon_%d_one_way_margin" , i)); |
1565 | array.push_back(polygon_dict); |
1566 | } |
1567 | dict["polygons" ] = array; |
1568 | |
1569 | return dict; |
1570 | } |
1571 | |
1572 | void TileDataCollisionEditor::_set_painted_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) { |
1573 | TileData *tile_data = p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile); |
1574 | ERR_FAIL_NULL(tile_data); |
1575 | |
1576 | polygon_editor->clear_polygons(); |
1577 | for (int i = 0; i < tile_data->get_collision_polygons_count(physics_layer); i++) { |
1578 | Vector<Vector2> polygon = tile_data->get_collision_polygon_points(physics_layer, i); |
1579 | if (polygon.size() >= 3) { |
1580 | polygon_editor->add_polygon(polygon); |
1581 | } |
1582 | } |
1583 | |
1584 | _polygons_changed(); |
1585 | dummy_object->set("linear_velocity" , tile_data->get_constant_linear_velocity(physics_layer)); |
1586 | dummy_object->set("angular_velocity" , tile_data->get_constant_angular_velocity(physics_layer)); |
1587 | for (int i = 0; i < tile_data->get_collision_polygons_count(physics_layer); i++) { |
1588 | dummy_object->set(vformat("polygon_%d_one_way" , i), tile_data->is_collision_polygon_one_way(physics_layer, i)); |
1589 | dummy_object->set(vformat("polygon_%d_one_way_margin" , i), tile_data->get_collision_polygon_one_way_margin(physics_layer, i)); |
1590 | } |
1591 | for (const KeyValue<StringName, EditorProperty *> &E : property_editors) { |
1592 | E.value->update_property(); |
1593 | } |
1594 | |
1595 | polygon_editor->set_background(p_tile_set_atlas_source->get_texture(), p_tile_set_atlas_source->get_tile_texture_region(p_coords), tile_data->get_texture_origin(), tile_data->get_flip_h(), tile_data->get_flip_v(), tile_data->get_transpose(), tile_data->get_modulate()); |
1596 | } |
1597 | |
1598 | void TileDataCollisionEditor::_set_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile, Variant p_value) { |
1599 | TileData *tile_data = p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile); |
1600 | ERR_FAIL_NULL(tile_data); |
1601 | |
1602 | Dictionary dict = p_value; |
1603 | tile_data->set_constant_linear_velocity(physics_layer, dict["linear_velocity" ]); |
1604 | tile_data->set_constant_angular_velocity(physics_layer, dict["angular_velocity" ]); |
1605 | Array array = dict["polygons" ]; |
1606 | tile_data->set_collision_polygons_count(physics_layer, array.size()); |
1607 | for (int i = 0; i < array.size(); i++) { |
1608 | Dictionary polygon_dict = array[i]; |
1609 | tile_data->set_collision_polygon_points(physics_layer, i, polygon_dict["points" ]); |
1610 | tile_data->set_collision_polygon_one_way(physics_layer, i, polygon_dict["one_way" ]); |
1611 | tile_data->set_collision_polygon_one_way_margin(physics_layer, i, polygon_dict["one_way_margin" ]); |
1612 | } |
1613 | |
1614 | polygon_editor->set_background(p_tile_set_atlas_source->get_texture(), p_tile_set_atlas_source->get_tile_texture_region(p_coords), tile_data->get_texture_origin(), tile_data->get_flip_h(), tile_data->get_flip_v(), tile_data->get_transpose(), tile_data->get_modulate()); |
1615 | } |
1616 | |
1617 | Variant TileDataCollisionEditor::_get_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) { |
1618 | TileData *tile_data = p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile); |
1619 | ERR_FAIL_NULL_V(tile_data, Variant()); |
1620 | |
1621 | Dictionary dict; |
1622 | dict["linear_velocity" ] = tile_data->get_constant_linear_velocity(physics_layer); |
1623 | dict["angular_velocity" ] = tile_data->get_constant_angular_velocity(physics_layer); |
1624 | Array array; |
1625 | for (int i = 0; i < tile_data->get_collision_polygons_count(physics_layer); i++) { |
1626 | Dictionary polygon_dict; |
1627 | polygon_dict["points" ] = tile_data->get_collision_polygon_points(physics_layer, i); |
1628 | polygon_dict["one_way" ] = tile_data->is_collision_polygon_one_way(physics_layer, i); |
1629 | polygon_dict["one_way_margin" ] = tile_data->get_collision_polygon_one_way_margin(physics_layer, i); |
1630 | array.push_back(polygon_dict); |
1631 | } |
1632 | dict["polygons" ] = array; |
1633 | return dict; |
1634 | } |
1635 | |
1636 | void TileDataCollisionEditor::_setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, HashMap<TileMapCell, Variant, TileMapCell> p_previous_values, Variant p_new_value) { |
1637 | Dictionary new_dict = p_new_value; |
1638 | EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); |
1639 | for (KeyValue<TileMapCell, Variant> &E : p_previous_values) { |
1640 | Vector2i coords = E.key.get_atlas_coords(); |
1641 | |
1642 | Dictionary old_dict = E.value; |
1643 | undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/linear_velocity" , coords.x, coords.y, E.key.alternative_tile, physics_layer), old_dict["linear_velocity" ]); |
1644 | undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/angular_velocity" , coords.x, coords.y, E.key.alternative_tile, physics_layer), old_dict["angular_velocity" ]); |
1645 | Array old_polygon_array = old_dict["polygons" ]; |
1646 | undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygons_count" , coords.x, coords.y, E.key.alternative_tile, physics_layer), old_polygon_array.size()); |
1647 | for (int i = 0; i < old_polygon_array.size(); i++) { |
1648 | Dictionary polygon_dict = old_polygon_array[i]; |
1649 | undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygon_%d/points" , coords.x, coords.y, E.key.alternative_tile, physics_layer, i), polygon_dict["points" ]); |
1650 | undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygon_%d/one_way" , coords.x, coords.y, E.key.alternative_tile, physics_layer, i), polygon_dict["one_way" ]); |
1651 | undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygon_%d/one_way_margin" , coords.x, coords.y, E.key.alternative_tile, physics_layer, i), polygon_dict["one_way_margin" ]); |
1652 | } |
1653 | |
1654 | undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/linear_velocity" , coords.x, coords.y, E.key.alternative_tile, physics_layer), new_dict["linear_velocity" ]); |
1655 | undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/angular_velocity" , coords.x, coords.y, E.key.alternative_tile, physics_layer), new_dict["angular_velocity" ]); |
1656 | Array new_polygon_array = new_dict["polygons" ]; |
1657 | undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygons_count" , coords.x, coords.y, E.key.alternative_tile, physics_layer), new_polygon_array.size()); |
1658 | for (int i = 0; i < new_polygon_array.size(); i++) { |
1659 | Dictionary polygon_dict = new_polygon_array[i]; |
1660 | undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygon_%d/points" , coords.x, coords.y, E.key.alternative_tile, physics_layer, i), polygon_dict["points" ]); |
1661 | undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygon_%d/one_way" , coords.x, coords.y, E.key.alternative_tile, physics_layer, i), polygon_dict["one_way" ]); |
1662 | undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygon_%d/one_way_margin" , coords.x, coords.y, E.key.alternative_tile, physics_layer, i), polygon_dict["one_way_margin" ]); |
1663 | } |
1664 | } |
1665 | } |
1666 | |
1667 | void TileDataCollisionEditor::_tile_set_changed() { |
1668 | polygon_editor->set_tile_set(tile_set); |
1669 | _polygons_changed(); |
1670 | } |
1671 | |
1672 | void TileDataCollisionEditor::_notification(int p_what) { |
1673 | switch (p_what) { |
1674 | case NOTIFICATION_ENTER_TREE: { |
1675 | polygon_editor->set_polygons_color(get_tree()->get_debug_collisions_color()); |
1676 | } break; |
1677 | } |
1678 | } |
1679 | |
1680 | TileDataCollisionEditor::TileDataCollisionEditor() { |
1681 | polygon_editor = memnew(GenericTilePolygonEditor); |
1682 | polygon_editor->set_multiple_polygon_mode(true); |
1683 | polygon_editor->connect("polygons_changed" , callable_mp(this, &TileDataCollisionEditor::_polygons_changed)); |
1684 | add_child(polygon_editor); |
1685 | |
1686 | dummy_object->add_dummy_property("linear_velocity" ); |
1687 | dummy_object->set("linear_velocity" , Vector2()); |
1688 | dummy_object->add_dummy_property("angular_velocity" ); |
1689 | dummy_object->set("angular_velocity" , 0.0); |
1690 | |
1691 | EditorProperty *linear_velocity_editor = EditorInspectorDefaultPlugin::get_editor_for_property(dummy_object, Variant::VECTOR2, "linear_velocity" , PROPERTY_HINT_NONE, "" , PROPERTY_USAGE_DEFAULT); |
1692 | linear_velocity_editor->set_object_and_property(dummy_object, "linear_velocity" ); |
1693 | linear_velocity_editor->set_label("linear_velocity" ); |
1694 | linear_velocity_editor->connect("property_changed" , callable_mp(this, &TileDataCollisionEditor::_property_value_changed).unbind(1)); |
1695 | linear_velocity_editor->connect("selected" , callable_mp(this, &TileDataCollisionEditor::_property_selected)); |
1696 | linear_velocity_editor->set_tooltip_text(linear_velocity_editor->get_edited_property()); |
1697 | linear_velocity_editor->update_property(); |
1698 | add_child(linear_velocity_editor); |
1699 | property_editors["linear_velocity" ] = linear_velocity_editor; |
1700 | |
1701 | EditorProperty *angular_velocity_editor = EditorInspectorDefaultPlugin::get_editor_for_property(dummy_object, Variant::FLOAT, "angular_velocity" , PROPERTY_HINT_NONE, "" , PROPERTY_USAGE_DEFAULT); |
1702 | angular_velocity_editor->set_object_and_property(dummy_object, "angular_velocity" ); |
1703 | angular_velocity_editor->set_label("angular_velocity" ); |
1704 | angular_velocity_editor->connect("property_changed" , callable_mp(this, &TileDataCollisionEditor::_property_value_changed).unbind(1)); |
1705 | angular_velocity_editor->connect("selected" , callable_mp(this, &TileDataCollisionEditor::_property_selected)); |
1706 | angular_velocity_editor->set_tooltip_text(angular_velocity_editor->get_edited_property()); |
1707 | angular_velocity_editor->update_property(); |
1708 | add_child(angular_velocity_editor); |
1709 | property_editors["angular_velocity" ] = angular_velocity_editor; |
1710 | |
1711 | _polygons_changed(); |
1712 | } |
1713 | |
1714 | TileDataCollisionEditor::~TileDataCollisionEditor() { |
1715 | memdelete(dummy_object); |
1716 | } |
1717 | |
1718 | void TileDataCollisionEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected) { |
1719 | TileData *tile_data = _get_tile_data(p_cell); |
1720 | ERR_FAIL_NULL(tile_data); |
1721 | |
1722 | // Draw all shapes. |
1723 | Vector<Color> color; |
1724 | if (p_selected) { |
1725 | Color grid_color = EDITOR_GET("editors/tiles_editor/grid_color" ); |
1726 | 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); |
1727 | selection_color.a = 0.7; |
1728 | color.push_back(selection_color); |
1729 | } else { |
1730 | Color debug_collision_color = p_canvas_item->get_tree()->get_debug_collisions_color(); |
1731 | color.push_back(debug_collision_color); |
1732 | } |
1733 | |
1734 | RenderingServer::get_singleton()->canvas_item_add_set_transform(p_canvas_item->get_canvas_item(), p_transform); |
1735 | |
1736 | Ref<Texture2D> one_way_icon = get_editor_theme_icon(SNAME("OneWayTile" )); |
1737 | for (int i = 0; i < tile_data->get_collision_polygons_count(physics_layer); i++) { |
1738 | Vector<Vector2> polygon = tile_data->get_collision_polygon_points(physics_layer, i); |
1739 | if (polygon.size() < 3) { |
1740 | continue; |
1741 | } |
1742 | |
1743 | p_canvas_item->draw_polygon(polygon, color); |
1744 | |
1745 | if (tile_data->is_collision_polygon_one_way(physics_layer, i)) { |
1746 | PackedVector2Array uvs; |
1747 | uvs.resize(polygon.size()); |
1748 | Vector2 size_1 = Vector2(1, 1) / tile_set->get_tile_size(); |
1749 | |
1750 | for (int j = 0; j < polygon.size(); j++) { |
1751 | uvs.write[j] = polygon[j] * size_1 + Vector2(0.5, 0.5); |
1752 | } |
1753 | |
1754 | Vector<Color> color2; |
1755 | color2.push_back(Color(1, 1, 1, 0.4)); |
1756 | p_canvas_item->draw_polygon(polygon, color2, uvs, one_way_icon); |
1757 | } |
1758 | } |
1759 | |
1760 | RenderingServer::get_singleton()->canvas_item_add_set_transform(p_canvas_item->get_canvas_item(), Transform2D()); |
1761 | } |
1762 | |
1763 | void TileDataTerrainsEditor::_update_terrain_selector() { |
1764 | ERR_FAIL_COND(!tile_set.is_valid()); |
1765 | |
1766 | // Update the terrain set selector. |
1767 | Vector<String> options; |
1768 | options.push_back(String(TTR("No terrains" )) + String(":-1" )); |
1769 | for (int i = 0; i < tile_set->get_terrain_sets_count(); i++) { |
1770 | options.push_back(vformat("Terrain Set %d" , i)); |
1771 | } |
1772 | terrain_set_property_editor->setup(options); |
1773 | terrain_set_property_editor->update_property(); |
1774 | |
1775 | // Update the terrain selector. |
1776 | int terrain_set = int(dummy_object->get("terrain_set" )); |
1777 | if (terrain_set == -1) { |
1778 | terrain_property_editor->hide(); |
1779 | } else { |
1780 | options.clear(); |
1781 | Vector<Vector<Ref<Texture2D>>> icons = tile_set->generate_terrains_icons(Size2(16, 16) * EDSCALE); |
1782 | options.push_back(String(TTR("No terrain" )) + String(":-1" )); |
1783 | for (int i = 0; i < tile_set->get_terrains_count(terrain_set); i++) { |
1784 | String name = tile_set->get_terrain_name(terrain_set, i); |
1785 | if (name.is_empty()) { |
1786 | options.push_back(vformat("Terrain %d" , i)); |
1787 | } else { |
1788 | options.push_back(name); |
1789 | } |
1790 | } |
1791 | terrain_property_editor->setup(options); |
1792 | terrain_property_editor->update_property(); |
1793 | |
1794 | // Kind of a hack to set icons. |
1795 | // We could provide a way to modify that in the EditorProperty. |
1796 | OptionButton *option_button = Object::cast_to<OptionButton>(terrain_property_editor->get_child(0)); |
1797 | for (int terrain = 0; terrain < tile_set->get_terrains_count(terrain_set); terrain++) { |
1798 | option_button->set_item_icon(terrain + 1, icons[terrain_set][terrain]); |
1799 | } |
1800 | terrain_property_editor->show(); |
1801 | } |
1802 | } |
1803 | |
1804 | void TileDataTerrainsEditor::_property_value_changed(StringName p_property, Variant p_value, StringName p_field) { |
1805 | Variant old_value = dummy_object->get(p_property); |
1806 | dummy_object->set(p_property, p_value); |
1807 | if (p_property == "terrain_set" ) { |
1808 | if (p_value != old_value) { |
1809 | dummy_object->set("terrain" , -1); |
1810 | } |
1811 | _update_terrain_selector(); |
1812 | } |
1813 | emit_signal(SNAME("needs_redraw" )); |
1814 | } |
1815 | |
1816 | void TileDataTerrainsEditor::_tile_set_changed() { |
1817 | ERR_FAIL_COND(!tile_set.is_valid()); |
1818 | |
1819 | // Fix if wrong values are selected. |
1820 | int terrain_set = int(dummy_object->get("terrain_set" )); |
1821 | if (terrain_set >= tile_set->get_terrain_sets_count()) { |
1822 | terrain_set = -1; |
1823 | dummy_object->set("terrain_set" , -1); |
1824 | } |
1825 | if (terrain_set >= 0) { |
1826 | if (int(dummy_object->get("terrain" )) >= tile_set->get_terrains_count(terrain_set)) { |
1827 | dummy_object->set("terrain" , -1); |
1828 | } |
1829 | } |
1830 | |
1831 | _update_terrain_selector(); |
1832 | } |
1833 | |
1834 | void TileDataTerrainsEditor::forward_draw_over_atlas(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_set_atlas_source, CanvasItem *p_canvas_item, Transform2D p_transform) { |
1835 | ERR_FAIL_COND(!tile_set.is_valid()); |
1836 | |
1837 | // Draw the hovered terrain bit, or the whole tile if it has the wrong terrain set. |
1838 | Vector2i hovered_coords = TileSetSource::INVALID_ATLAS_COORDS; |
1839 | if (drag_type == DRAG_TYPE_NONE) { |
1840 | Vector2i mouse_pos = p_transform.affine_inverse().xform(p_canvas_item->get_local_mouse_position()); |
1841 | hovered_coords = p_tile_atlas_view->get_atlas_tile_coords_at_pos(mouse_pos); |
1842 | hovered_coords = p_tile_set_atlas_source->get_tile_at_coords(hovered_coords); |
1843 | if (hovered_coords != TileSetSource::INVALID_ATLAS_COORDS) { |
1844 | TileData *tile_data = p_tile_set_atlas_source->get_tile_data(hovered_coords, 0); |
1845 | int terrain_set = tile_data->get_terrain_set(); |
1846 | Rect2i texture_region = p_tile_set_atlas_source->get_tile_texture_region(hovered_coords); |
1847 | Vector2i position = texture_region.get_center() + tile_data->get_texture_origin(); |
1848 | |
1849 | if (terrain_set >= 0 && terrain_set == int(dummy_object->get("terrain_set" ))) { |
1850 | // Draw hovered bit. |
1851 | Transform2D xform; |
1852 | xform.set_origin(position); |
1853 | |
1854 | Vector<Color> color; |
1855 | color.push_back(Color(1.0, 1.0, 1.0, 0.5)); |
1856 | |
1857 | Vector<Vector2> polygon = tile_set->get_terrain_polygon(terrain_set); |
1858 | if (Geometry2D::is_point_in_polygon(xform.affine_inverse().xform(mouse_pos), polygon)) { |
1859 | p_canvas_item->draw_set_transform_matrix(p_transform * xform); |
1860 | p_canvas_item->draw_polygon(polygon, color); |
1861 | } |
1862 | for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { |
1863 | TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); |
1864 | if (tile_set->is_valid_terrain_peering_bit(terrain_set, bit)) { |
1865 | polygon = tile_set->get_terrain_peering_bit_polygon(terrain_set, bit); |
1866 | if (Geometry2D::is_point_in_polygon(xform.affine_inverse().xform(mouse_pos), polygon)) { |
1867 | p_canvas_item->draw_set_transform_matrix(p_transform * xform); |
1868 | p_canvas_item->draw_polygon(polygon, color); |
1869 | } |
1870 | } |
1871 | } |
1872 | } else { |
1873 | // Draw hovered tile. |
1874 | Transform2D tile_xform; |
1875 | tile_xform.set_origin(position); |
1876 | tile_xform.set_scale(tile_set->get_tile_size()); |
1877 | tile_set->draw_tile_shape(p_canvas_item, p_transform * tile_xform, Color(1.0, 1.0, 1.0, 0.5), true); |
1878 | } |
1879 | } |
1880 | } |
1881 | |
1882 | // Dim terrains with wrong terrain set. |
1883 | Ref<Font> font = TileSetEditor::get_singleton()->get_theme_font(SNAME("bold" ), EditorStringName(EditorFonts)); |
1884 | int font_size = TileSetEditor::get_singleton()->get_theme_font_size(SNAME("bold_size" ), EditorStringName(EditorFonts)); |
1885 | for (int i = 0; i < p_tile_set_atlas_source->get_tiles_count(); i++) { |
1886 | Vector2i coords = p_tile_set_atlas_source->get_tile_id(i); |
1887 | if (coords != hovered_coords) { |
1888 | TileData *tile_data = p_tile_set_atlas_source->get_tile_data(coords, 0); |
1889 | if (tile_data->get_terrain_set() != int(dummy_object->get("terrain_set" ))) { |
1890 | // Dimming |
1891 | p_canvas_item->draw_set_transform_matrix(p_transform); |
1892 | Rect2i rect = p_tile_set_atlas_source->get_tile_texture_region(coords); |
1893 | p_canvas_item->draw_rect(rect, Color(0.0, 0.0, 0.0, 0.3)); |
1894 | |
1895 | // Text |
1896 | p_canvas_item->draw_set_transform_matrix(Transform2D()); |
1897 | Rect2i texture_region = p_tile_set_atlas_source->get_tile_texture_region(coords); |
1898 | Vector2i position = texture_region.get_center() + tile_data->get_texture_origin(); |
1899 | |
1900 | Color color = Color(1, 1, 1); |
1901 | String text; |
1902 | if (tile_data->get_terrain_set() >= 0) { |
1903 | text = vformat("%d" , tile_data->get_terrain_set()); |
1904 | } else { |
1905 | text = "-" ; |
1906 | } |
1907 | Vector2 string_size = font->get_string_size(text, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size); |
1908 | p_canvas_item->draw_string_outline(font, p_transform.xform(position) + Vector2i(-string_size.x / 2, string_size.y / 2), text, HORIZONTAL_ALIGNMENT_CENTER, string_size.x, font_size, 1, Color(0, 0, 0, 1)); |
1909 | p_canvas_item->draw_string(font, p_transform.xform(position) + Vector2i(-string_size.x / 2, string_size.y / 2), text, HORIZONTAL_ALIGNMENT_CENTER, string_size.x, font_size, color); |
1910 | } |
1911 | } |
1912 | } |
1913 | p_canvas_item->draw_set_transform_matrix(Transform2D()); |
1914 | |
1915 | if (drag_type == DRAG_TYPE_PAINT_TERRAIN_SET_RECT) { |
1916 | // Draw selection rectangle. |
1917 | Color grid_color = EDITOR_GET("editors/tiles_editor/grid_color" ); |
1918 | 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); |
1919 | |
1920 | p_canvas_item->draw_set_transform_matrix(p_transform); |
1921 | |
1922 | Rect2i rect; |
1923 | rect.set_position(p_tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_pos, true)); |
1924 | rect.set_end(p_tile_atlas_view->get_atlas_tile_coords_at_pos(p_transform.affine_inverse().xform(p_canvas_item->get_local_mouse_position()), true)); |
1925 | rect = rect.abs(); |
1926 | |
1927 | RBSet<TileMapCell> edited; |
1928 | for (int x = rect.get_position().x; x <= rect.get_end().x; x++) { |
1929 | for (int y = rect.get_position().y; y <= rect.get_end().y; y++) { |
1930 | Vector2i coords = Vector2i(x, y); |
1931 | coords = p_tile_set_atlas_source->get_tile_at_coords(coords); |
1932 | if (coords != TileSetSource::INVALID_ATLAS_COORDS) { |
1933 | TileMapCell cell; |
1934 | cell.source_id = 0; |
1935 | cell.set_atlas_coords(coords); |
1936 | cell.alternative_tile = 0; |
1937 | edited.insert(cell); |
1938 | } |
1939 | } |
1940 | } |
1941 | |
1942 | for (const TileMapCell &E : edited) { |
1943 | Vector2i coords = E.get_atlas_coords(); |
1944 | p_canvas_item->draw_rect(p_tile_set_atlas_source->get_tile_texture_region(coords), selection_color, false); |
1945 | } |
1946 | p_canvas_item->draw_set_transform_matrix(Transform2D()); |
1947 | } else if (drag_type == DRAG_TYPE_PAINT_TERRAIN_BITS_RECT) { |
1948 | // Highlight selected peering bits. |
1949 | Dictionary painted = Dictionary(drag_painted_value); |
1950 | int terrain_set = int(painted["terrain_set" ]); |
1951 | |
1952 | Rect2i rect; |
1953 | rect.set_position(p_tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_pos, true)); |
1954 | rect.set_end(p_tile_atlas_view->get_atlas_tile_coords_at_pos(p_transform.affine_inverse().xform(p_canvas_item->get_local_mouse_position()), true)); |
1955 | rect = rect.abs(); |
1956 | |
1957 | RBSet<TileMapCell> edited; |
1958 | for (int x = rect.get_position().x; x <= rect.get_end().x; x++) { |
1959 | for (int y = rect.get_position().y; y <= rect.get_end().y; y++) { |
1960 | Vector2i coords = Vector2i(x, y); |
1961 | coords = p_tile_set_atlas_source->get_tile_at_coords(coords); |
1962 | if (coords != TileSetSource::INVALID_ATLAS_COORDS) { |
1963 | TileData *tile_data = p_tile_set_atlas_source->get_tile_data(coords, 0); |
1964 | if (tile_data->get_terrain_set() == terrain_set) { |
1965 | TileMapCell cell; |
1966 | cell.source_id = 0; |
1967 | cell.set_atlas_coords(coords); |
1968 | cell.alternative_tile = 0; |
1969 | edited.insert(cell); |
1970 | } |
1971 | } |
1972 | } |
1973 | } |
1974 | |
1975 | Vector2 end = p_transform.affine_inverse().xform(p_canvas_item->get_local_mouse_position()); |
1976 | Vector<Point2> mouse_pos_rect_polygon; |
1977 | mouse_pos_rect_polygon.push_back(drag_start_pos); |
1978 | mouse_pos_rect_polygon.push_back(Vector2(end.x, drag_start_pos.y)); |
1979 | mouse_pos_rect_polygon.push_back(end); |
1980 | mouse_pos_rect_polygon.push_back(Vector2(drag_start_pos.x, end.y)); |
1981 | |
1982 | Vector<Color> color; |
1983 | color.push_back(Color(1.0, 1.0, 1.0, 0.5)); |
1984 | |
1985 | p_canvas_item->draw_set_transform_matrix(p_transform); |
1986 | |
1987 | for (const TileMapCell &E : edited) { |
1988 | Vector2i coords = E.get_atlas_coords(); |
1989 | |
1990 | Rect2i texture_region = p_tile_set_atlas_source->get_tile_texture_region(coords); |
1991 | Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_data(coords, 0)->get_texture_origin(); |
1992 | |
1993 | Vector<Vector2> polygon = tile_set->get_terrain_polygon(terrain_set); |
1994 | for (int j = 0; j < polygon.size(); j++) { |
1995 | polygon.write[j] += position; |
1996 | } |
1997 | if (!Geometry2D::intersect_polygons(polygon, mouse_pos_rect_polygon).is_empty()) { |
1998 | // Draw terrain. |
1999 | p_canvas_item->draw_polygon(polygon, color); |
2000 | } |
2001 | |
2002 | for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { |
2003 | TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); |
2004 | if (tile_set->is_valid_terrain_peering_bit(terrain_set, bit)) { |
2005 | polygon = tile_set->get_terrain_peering_bit_polygon(terrain_set, bit); |
2006 | for (int j = 0; j < polygon.size(); j++) { |
2007 | polygon.write[j] += position; |
2008 | } |
2009 | if (!Geometry2D::intersect_polygons(polygon, mouse_pos_rect_polygon).is_empty()) { |
2010 | // Draw bit. |
2011 | p_canvas_item->draw_polygon(polygon, color); |
2012 | } |
2013 | } |
2014 | } |
2015 | } |
2016 | |
2017 | p_canvas_item->draw_set_transform_matrix(Transform2D()); |
2018 | } |
2019 | } |
2020 | |
2021 | void TileDataTerrainsEditor::forward_draw_over_alternatives(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_set_atlas_source, CanvasItem *p_canvas_item, Transform2D p_transform) { |
2022 | ERR_FAIL_COND(!tile_set.is_valid()); |
2023 | |
2024 | // Draw the hovered terrain bit, or the whole tile if it has the wrong terrain set. |
2025 | Vector2i hovered_coords = TileSetSource::INVALID_ATLAS_COORDS; |
2026 | int hovered_alternative = TileSetSource::INVALID_TILE_ALTERNATIVE; |
2027 | if (drag_type == DRAG_TYPE_NONE) { |
2028 | Vector2i mouse_pos = p_transform.affine_inverse().xform(p_canvas_item->get_local_mouse_position()); |
2029 | Vector3i hovered = p_tile_atlas_view->get_alternative_tile_at_pos(mouse_pos); |
2030 | hovered_coords = Vector2i(hovered.x, hovered.y); |
2031 | hovered_alternative = hovered.z; |
2032 | if (hovered_coords != TileSetSource::INVALID_ATLAS_COORDS) { |
2033 | TileData *tile_data = p_tile_set_atlas_source->get_tile_data(hovered_coords, hovered_alternative); |
2034 | int terrain_set = tile_data->get_terrain_set(); |
2035 | Rect2i texture_region = p_tile_atlas_view->get_alternative_tile_rect(hovered_coords, hovered_alternative); |
2036 | Vector2i position = texture_region.get_center() + tile_data->get_texture_origin(); |
2037 | |
2038 | if (terrain_set == int(dummy_object->get("terrain_set" ))) { |
2039 | // Draw hovered bit. |
2040 | Transform2D xform; |
2041 | xform.set_origin(position); |
2042 | |
2043 | Vector<Color> color; |
2044 | color.push_back(Color(1.0, 1.0, 1.0, 0.5)); |
2045 | |
2046 | Vector<Vector2> polygon = tile_set->get_terrain_polygon(terrain_set); |
2047 | if (Geometry2D::is_point_in_polygon(xform.affine_inverse().xform(mouse_pos), polygon)) { |
2048 | p_canvas_item->draw_set_transform_matrix(p_transform * xform); |
2049 | p_canvas_item->draw_polygon(polygon, color); |
2050 | } |
2051 | |
2052 | for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { |
2053 | TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); |
2054 | if (tile_set->is_valid_terrain_peering_bit(terrain_set, bit)) { |
2055 | polygon = tile_set->get_terrain_peering_bit_polygon(terrain_set, bit); |
2056 | if (Geometry2D::is_point_in_polygon(xform.affine_inverse().xform(mouse_pos), polygon)) { |
2057 | p_canvas_item->draw_set_transform_matrix(p_transform * xform); |
2058 | p_canvas_item->draw_polygon(polygon, color); |
2059 | } |
2060 | } |
2061 | } |
2062 | } else { |
2063 | // Draw hovered tile. |
2064 | Transform2D tile_xform; |
2065 | tile_xform.set_origin(position); |
2066 | tile_xform.set_scale(tile_set->get_tile_size()); |
2067 | tile_set->draw_tile_shape(p_canvas_item, p_transform * tile_xform, Color(1.0, 1.0, 1.0, 0.5), true); |
2068 | } |
2069 | } |
2070 | } |
2071 | |
2072 | // Dim terrains with wrong terrain set. |
2073 | Ref<Font> font = TileSetEditor::get_singleton()->get_theme_font(SNAME("bold" ), EditorStringName(EditorFonts)); |
2074 | int font_size = TileSetEditor::get_singleton()->get_theme_font_size(SNAME("bold_size" ), EditorStringName(EditorFonts)); |
2075 | for (int i = 0; i < p_tile_set_atlas_source->get_tiles_count(); i++) { |
2076 | Vector2i coords = p_tile_set_atlas_source->get_tile_id(i); |
2077 | for (int j = 1; j < p_tile_set_atlas_source->get_alternative_tiles_count(coords); j++) { |
2078 | int alternative_tile = p_tile_set_atlas_source->get_alternative_tile_id(coords, j); |
2079 | if (coords != hovered_coords || alternative_tile != hovered_alternative) { |
2080 | TileData *tile_data = p_tile_set_atlas_source->get_tile_data(coords, alternative_tile); |
2081 | if (tile_data->get_terrain_set() != int(dummy_object->get("terrain_set" ))) { |
2082 | // Dimming |
2083 | p_canvas_item->draw_set_transform_matrix(p_transform); |
2084 | Rect2i rect = p_tile_atlas_view->get_alternative_tile_rect(coords, alternative_tile); |
2085 | p_canvas_item->draw_rect(rect, Color(0.0, 0.0, 0.0, 0.3)); |
2086 | |
2087 | // Text |
2088 | p_canvas_item->draw_set_transform_matrix(Transform2D()); |
2089 | Rect2i texture_region = p_tile_atlas_view->get_alternative_tile_rect(coords, alternative_tile); |
2090 | Vector2i position = texture_region.get_center() + tile_data->get_texture_origin(); |
2091 | |
2092 | Color color = Color(1, 1, 1); |
2093 | String text; |
2094 | if (tile_data->get_terrain_set() >= 0) { |
2095 | text = vformat("%d" , tile_data->get_terrain_set()); |
2096 | } else { |
2097 | text = "-" ; |
2098 | } |
2099 | Vector2 string_size = font->get_string_size(text, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size); |
2100 | p_canvas_item->draw_string_outline(font, p_transform.xform(position) + Vector2i(-string_size.x / 2, string_size.y / 2), text, HORIZONTAL_ALIGNMENT_CENTER, string_size.x, font_size, 1, Color(0, 0, 0, 1)); |
2101 | p_canvas_item->draw_string(font, p_transform.xform(position) + Vector2i(-string_size.x / 2, string_size.y / 2), text, HORIZONTAL_ALIGNMENT_CENTER, string_size.x, font_size, color); |
2102 | } |
2103 | } |
2104 | } |
2105 | } |
2106 | |
2107 | p_canvas_item->draw_set_transform_matrix(Transform2D()); |
2108 | } |
2109 | |
2110 | void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_set_atlas_source, const Ref<InputEvent> &p_event) { |
2111 | Ref<InputEventMouseMotion> mm = p_event; |
2112 | if (mm.is_valid()) { |
2113 | if (drag_type == DRAG_TYPE_PAINT_TERRAIN_SET) { |
2114 | Vector<Vector2i> line = Geometry2D::bresenham_line(p_tile_atlas_view->get_atlas_tile_coords_at_pos(drag_last_pos, true), p_tile_atlas_view->get_atlas_tile_coords_at_pos(mm->get_position(), true)); |
2115 | for (int i = 0; i < line.size(); i++) { |
2116 | Vector2i coords = p_tile_set_atlas_source->get_tile_at_coords(line[i]); |
2117 | if (coords != TileSetSource::INVALID_ATLAS_COORDS) { |
2118 | int terrain_set = drag_painted_value; |
2119 | TileMapCell cell; |
2120 | cell.source_id = 0; |
2121 | cell.set_atlas_coords(coords); |
2122 | cell.alternative_tile = 0; |
2123 | |
2124 | // Save the old terrain_set and terrains bits. |
2125 | TileData *tile_data = p_tile_set_atlas_source->get_tile_data(coords, 0); |
2126 | if (!drag_modified.has(cell)) { |
2127 | Dictionary dict; |
2128 | dict["terrain_set" ] = tile_data->get_terrain_set(); |
2129 | dict["terrain" ] = tile_data->get_terrain(); |
2130 | Array array; |
2131 | for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) { |
2132 | TileSet::CellNeighbor bit = TileSet::CellNeighbor(j); |
2133 | array.push_back(tile_data->is_valid_terrain_peering_bit(bit) ? tile_data->get_terrain_peering_bit(bit) : -1); |
2134 | } |
2135 | dict["terrain_peering_bits" ] = array; |
2136 | drag_modified[cell] = dict; |
2137 | } |
2138 | |
2139 | // Set the terrain_set. |
2140 | tile_data->set_terrain_set(terrain_set); |
2141 | } |
2142 | } |
2143 | drag_last_pos = mm->get_position(); |
2144 | accept_event(); |
2145 | } else if (drag_type == DRAG_TYPE_PAINT_TERRAIN_BITS) { |
2146 | int terrain_set = Dictionary(drag_painted_value)["terrain_set" ]; |
2147 | int terrain = Dictionary(drag_painted_value)["terrain" ]; |
2148 | Vector<Vector2i> line = Geometry2D::bresenham_line(p_tile_atlas_view->get_atlas_tile_coords_at_pos(drag_last_pos, true), p_tile_atlas_view->get_atlas_tile_coords_at_pos(mm->get_position(), true)); |
2149 | for (int i = 0; i < line.size(); i++) { |
2150 | Vector2i coords = p_tile_set_atlas_source->get_tile_at_coords(line[i]); |
2151 | if (coords != TileSetSource::INVALID_ATLAS_COORDS) { |
2152 | TileMapCell cell; |
2153 | cell.source_id = 0; |
2154 | cell.set_atlas_coords(coords); |
2155 | cell.alternative_tile = 0; |
2156 | |
2157 | TileData *tile_data = p_tile_set_atlas_source->get_tile_data(coords, 0); |
2158 | if (tile_data->get_terrain_set() == terrain_set) { |
2159 | // Save the old terrain_set and terrains bits. |
2160 | if (!drag_modified.has(cell)) { |
2161 | Dictionary dict; |
2162 | dict["terrain_set" ] = tile_data->get_terrain_set(); |
2163 | dict["terrain" ] = tile_data->get_terrain(); |
2164 | Array array; |
2165 | for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) { |
2166 | TileSet::CellNeighbor bit = TileSet::CellNeighbor(j); |
2167 | array.push_back(tile_data->is_valid_terrain_peering_bit(bit) ? tile_data->get_terrain_peering_bit(bit) : -1); |
2168 | } |
2169 | dict["terrain_peering_bits" ] = array; |
2170 | drag_modified[cell] = dict; |
2171 | } |
2172 | |
2173 | // Set the terrains bits. |
2174 | Rect2i texture_region = p_tile_set_atlas_source->get_tile_texture_region(coords); |
2175 | Vector2i position = texture_region.get_center() + tile_data->get_texture_origin(); |
2176 | |
2177 | Vector<Vector2> polygon = tile_set->get_terrain_polygon(tile_data->get_terrain_set()); |
2178 | if (Geometry2D::is_segment_intersecting_polygon(mm->get_position() - position, drag_last_pos - position, polygon)) { |
2179 | tile_data->set_terrain(terrain); |
2180 | } |
2181 | for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) { |
2182 | TileSet::CellNeighbor bit = TileSet::CellNeighbor(j); |
2183 | if (tile_data->is_valid_terrain_peering_bit(bit)) { |
2184 | polygon = tile_set->get_terrain_peering_bit_polygon(tile_data->get_terrain_set(), bit); |
2185 | if (Geometry2D::is_segment_intersecting_polygon(mm->get_position() - position, drag_last_pos - position, polygon)) { |
2186 | tile_data->set_terrain_peering_bit(bit, terrain); |
2187 | } |
2188 | } |
2189 | } |
2190 | } |
2191 | } |
2192 | } |
2193 | drag_last_pos = mm->get_position(); |
2194 | accept_event(); |
2195 | } |
2196 | } |
2197 | |
2198 | Ref<InputEventMouseButton> mb = p_event; |
2199 | if (mb.is_valid()) { |
2200 | if (mb->get_button_index() == MouseButton::LEFT || mb->get_button_index() == MouseButton::RIGHT) { |
2201 | if (mb->is_pressed()) { |
2202 | if (picker_button->is_pressed() || (mb->is_command_or_control_pressed() && !mb->is_shift_pressed())) { |
2203 | Vector2i coords = p_tile_atlas_view->get_atlas_tile_coords_at_pos(mb->get_position()); |
2204 | coords = p_tile_set_atlas_source->get_tile_at_coords(coords); |
2205 | if (coords != TileSetSource::INVALID_ATLAS_COORDS) { |
2206 | TileData *tile_data = p_tile_set_atlas_source->get_tile_data(coords, 0); |
2207 | int terrain_set = tile_data->get_terrain_set(); |
2208 | Rect2i texture_region = p_tile_set_atlas_source->get_tile_texture_region(coords); |
2209 | Vector2i position = texture_region.get_center() + tile_data->get_texture_origin(); |
2210 | dummy_object->set("terrain_set" , terrain_set); |
2211 | dummy_object->set("terrain" , -1); |
2212 | |
2213 | Vector<Vector2> polygon = tile_set->get_terrain_polygon(terrain_set); |
2214 | if (Geometry2D::is_point_in_polygon(mb->get_position() - position, polygon)) { |
2215 | dummy_object->set("terrain" , tile_data->get_terrain()); |
2216 | } |
2217 | for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { |
2218 | TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); |
2219 | if (tile_set->is_valid_terrain_peering_bit(terrain_set, bit)) { |
2220 | polygon = tile_set->get_terrain_peering_bit_polygon(terrain_set, bit); |
2221 | if (Geometry2D::is_point_in_polygon(mb->get_position() - position, polygon)) { |
2222 | dummy_object->set("terrain" , tile_data->get_terrain_peering_bit(bit)); |
2223 | } |
2224 | } |
2225 | } |
2226 | terrain_set_property_editor->update_property(); |
2227 | _update_terrain_selector(); |
2228 | picker_button->set_pressed(false); |
2229 | accept_event(); |
2230 | } |
2231 | } else { |
2232 | Vector2i coords = p_tile_atlas_view->get_atlas_tile_coords_at_pos(mb->get_position()); |
2233 | coords = p_tile_set_atlas_source->get_tile_at_coords(coords); |
2234 | TileData *tile_data = nullptr; |
2235 | if (coords != TileSetAtlasSource::INVALID_ATLAS_COORDS) { |
2236 | tile_data = p_tile_set_atlas_source->get_tile_data(coords, 0); |
2237 | } |
2238 | int terrain_set = int(dummy_object->get("terrain_set" )); |
2239 | int terrain = int(dummy_object->get("terrain" )); |
2240 | if (terrain_set == -1 || !tile_data || tile_data->get_terrain_set() != terrain_set) { |
2241 | // Paint terrain sets. |
2242 | if (mb->get_button_index() == MouseButton::RIGHT) { |
2243 | terrain_set = -1; |
2244 | } |
2245 | if (mb->is_command_or_control_pressed() && mb->is_shift_pressed()) { |
2246 | // Paint terrain set with rect. |
2247 | drag_type = DRAG_TYPE_PAINT_TERRAIN_SET_RECT; |
2248 | drag_modified.clear(); |
2249 | drag_painted_value = terrain_set; |
2250 | drag_start_pos = mb->get_position(); |
2251 | } else { |
2252 | // Paint terrain set. |
2253 | drag_type = DRAG_TYPE_PAINT_TERRAIN_SET; |
2254 | drag_modified.clear(); |
2255 | drag_painted_value = terrain_set; |
2256 | |
2257 | if (coords != TileSetSource::INVALID_ATLAS_COORDS) { |
2258 | TileMapCell cell; |
2259 | cell.source_id = 0; |
2260 | cell.set_atlas_coords(coords); |
2261 | cell.alternative_tile = 0; |
2262 | |
2263 | // Save the old terrain_set and terrains bits. |
2264 | Dictionary dict; |
2265 | dict["terrain_set" ] = tile_data->get_terrain_set(); |
2266 | dict["terrain" ] = tile_data->get_terrain(); |
2267 | Array array; |
2268 | for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { |
2269 | TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); |
2270 | array.push_back(tile_data->is_valid_terrain_peering_bit(bit) ? tile_data->get_terrain_peering_bit(bit) : -1); |
2271 | } |
2272 | dict["terrain_peering_bits" ] = array; |
2273 | drag_modified[cell] = dict; |
2274 | |
2275 | // Set the terrain_set. |
2276 | tile_data->set_terrain_set(terrain_set); |
2277 | } |
2278 | drag_last_pos = mb->get_position(); |
2279 | } |
2280 | accept_event(); |
2281 | } else if (tile_data->get_terrain_set() == terrain_set) { |
2282 | // Paint terrain bits. |
2283 | if (mb->get_button_index() == MouseButton::RIGHT) { |
2284 | terrain = -1; |
2285 | } |
2286 | if (mb->is_command_or_control_pressed() && mb->is_shift_pressed()) { |
2287 | // Paint terrain bits with rect. |
2288 | drag_type = DRAG_TYPE_PAINT_TERRAIN_BITS_RECT; |
2289 | drag_modified.clear(); |
2290 | Dictionary painted_dict; |
2291 | painted_dict["terrain_set" ] = terrain_set; |
2292 | painted_dict["terrain" ] = terrain; |
2293 | drag_painted_value = painted_dict; |
2294 | drag_start_pos = mb->get_position(); |
2295 | } else { |
2296 | // Paint terrain bits. |
2297 | drag_type = DRAG_TYPE_PAINT_TERRAIN_BITS; |
2298 | drag_modified.clear(); |
2299 | Dictionary painted_dict; |
2300 | painted_dict["terrain_set" ] = terrain_set; |
2301 | painted_dict["terrain" ] = terrain; |
2302 | drag_painted_value = painted_dict; |
2303 | |
2304 | if (coords != TileSetSource::INVALID_ATLAS_COORDS) { |
2305 | TileMapCell cell; |
2306 | cell.source_id = 0; |
2307 | cell.set_atlas_coords(coords); |
2308 | cell.alternative_tile = 0; |
2309 | |
2310 | // Save the old terrain_set and terrains bits. |
2311 | Dictionary dict; |
2312 | dict["terrain_set" ] = tile_data->get_terrain_set(); |
2313 | dict["terrain" ] = tile_data->get_terrain(); |
2314 | Array array; |
2315 | for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { |
2316 | TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); |
2317 | array.push_back(tile_data->is_valid_terrain_peering_bit(bit) ? tile_data->get_terrain_peering_bit(bit) : -1); |
2318 | } |
2319 | dict["terrain_peering_bits" ] = array; |
2320 | drag_modified[cell] = dict; |
2321 | |
2322 | // Set the terrain bit. |
2323 | Rect2i texture_region = p_tile_set_atlas_source->get_tile_texture_region(coords); |
2324 | Vector2i position = texture_region.get_center() + tile_data->get_texture_origin(); |
2325 | |
2326 | Vector<Vector2> polygon = tile_set->get_terrain_polygon(terrain_set); |
2327 | if (Geometry2D::is_point_in_polygon(mb->get_position() - position, polygon)) { |
2328 | tile_data->set_terrain(terrain); |
2329 | } |
2330 | for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { |
2331 | TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); |
2332 | if (tile_set->is_valid_terrain_peering_bit(terrain_set, bit)) { |
2333 | polygon = tile_set->get_terrain_peering_bit_polygon(terrain_set, bit); |
2334 | if (Geometry2D::is_point_in_polygon(mb->get_position() - position, polygon)) { |
2335 | tile_data->set_terrain_peering_bit(bit, terrain); |
2336 | } |
2337 | } |
2338 | } |
2339 | } |
2340 | drag_last_pos = mb->get_position(); |
2341 | } |
2342 | accept_event(); |
2343 | } |
2344 | } |
2345 | } else { |
2346 | EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); |
2347 | if (drag_type == DRAG_TYPE_PAINT_TERRAIN_SET_RECT) { |
2348 | Rect2i rect; |
2349 | rect.set_position(p_tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_pos, true)); |
2350 | rect.set_end(p_tile_atlas_view->get_atlas_tile_coords_at_pos(mb->get_position(), true)); |
2351 | rect = rect.abs(); |
2352 | |
2353 | RBSet<TileMapCell> edited; |
2354 | for (int x = rect.get_position().x; x <= rect.get_end().x; x++) { |
2355 | for (int y = rect.get_position().y; y <= rect.get_end().y; y++) { |
2356 | Vector2i coords = Vector2i(x, y); |
2357 | coords = p_tile_set_atlas_source->get_tile_at_coords(coords); |
2358 | if (coords != TileSetSource::INVALID_ATLAS_COORDS) { |
2359 | TileMapCell cell; |
2360 | cell.source_id = 0; |
2361 | cell.set_atlas_coords(coords); |
2362 | cell.alternative_tile = 0; |
2363 | edited.insert(cell); |
2364 | } |
2365 | } |
2366 | } |
2367 | undo_redo->create_action(TTR("Painting Terrain Set" )); |
2368 | for (const TileMapCell &E : edited) { |
2369 | Vector2i coords = E.get_atlas_coords(); |
2370 | TileData *tile_data = p_tile_set_atlas_source->get_tile_data(coords, 0); |
2371 | undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain_set" , coords.x, coords.y, E.alternative_tile), drag_painted_value); |
2372 | undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain_set" , coords.x, coords.y, E.alternative_tile), tile_data->get_terrain_set()); |
2373 | if (tile_data->get_terrain_set() >= 0) { |
2374 | undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain" , coords.x, coords.y, E.alternative_tile), tile_data->get_terrain()); |
2375 | for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { |
2376 | TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); |
2377 | if (tile_data->is_valid_terrain_peering_bit(bit)) { |
2378 | undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E.alternative_tile), tile_data->get_terrain_peering_bit(bit)); |
2379 | } |
2380 | } |
2381 | } |
2382 | } |
2383 | undo_redo->commit_action(true); |
2384 | drag_type = DRAG_TYPE_NONE; |
2385 | accept_event(); |
2386 | } else if (drag_type == DRAG_TYPE_PAINT_TERRAIN_SET) { |
2387 | undo_redo->create_action(TTR("Painting Terrain Set" )); |
2388 | for (KeyValue<TileMapCell, Variant> &E : drag_modified) { |
2389 | Dictionary dict = E.value; |
2390 | Vector2i coords = E.key.get_atlas_coords(); |
2391 | undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain_set" , coords.x, coords.y, E.key.alternative_tile), drag_painted_value); |
2392 | undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain_set" , coords.x, coords.y, E.key.alternative_tile), dict["terrain_set" ]); |
2393 | if (int(dict["terrain_set" ]) >= 0) { |
2394 | undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain" , coords.x, coords.y, E.key.alternative_tile), dict["terrain" ]); |
2395 | Array array = dict["terrain_peering_bits" ]; |
2396 | for (int i = 0; i < array.size(); i++) { |
2397 | TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); |
2398 | if (tile_set->is_valid_terrain_peering_bit(dict["terrain_set" ], bit)) { |
2399 | undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E.key.alternative_tile), array[i]); |
2400 | } |
2401 | } |
2402 | } |
2403 | } |
2404 | undo_redo->commit_action(false); |
2405 | drag_type = DRAG_TYPE_NONE; |
2406 | accept_event(); |
2407 | } else if (drag_type == DRAG_TYPE_PAINT_TERRAIN_BITS) { |
2408 | Dictionary painted = Dictionary(drag_painted_value); |
2409 | int terrain_set = int(painted["terrain_set" ]); |
2410 | int terrain = int(painted["terrain" ]); |
2411 | undo_redo->create_action(TTR("Painting Terrain" )); |
2412 | for (KeyValue<TileMapCell, Variant> &E : drag_modified) { |
2413 | Dictionary dict = E.value; |
2414 | Vector2i coords = E.key.get_atlas_coords(); |
2415 | undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain" , coords.x, coords.y, E.key.alternative_tile), terrain); |
2416 | undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain" , coords.x, coords.y, E.key.alternative_tile), dict["terrain" ]); |
2417 | Array array = dict["terrain_peering_bits" ]; |
2418 | for (int i = 0; i < array.size(); i++) { |
2419 | TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); |
2420 | if (tile_set->is_valid_terrain_peering_bit(terrain_set, bit)) { |
2421 | undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E.key.alternative_tile), terrain); |
2422 | } |
2423 | if (tile_set->is_valid_terrain_peering_bit(dict["terrain_set" ], bit)) { |
2424 | undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E.key.alternative_tile), array[i]); |
2425 | } |
2426 | } |
2427 | } |
2428 | undo_redo->commit_action(false); |
2429 | drag_type = DRAG_TYPE_NONE; |
2430 | accept_event(); |
2431 | } else if (drag_type == DRAG_TYPE_PAINT_TERRAIN_BITS_RECT) { |
2432 | Dictionary painted = Dictionary(drag_painted_value); |
2433 | int terrain_set = int(painted["terrain_set" ]); |
2434 | int terrain = int(painted["terrain" ]); |
2435 | |
2436 | Rect2i rect; |
2437 | rect.set_position(p_tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_pos, true)); |
2438 | rect.set_end(p_tile_atlas_view->get_atlas_tile_coords_at_pos(mb->get_position(), true)); |
2439 | rect = rect.abs(); |
2440 | |
2441 | RBSet<TileMapCell> edited; |
2442 | for (int x = rect.get_position().x; x <= rect.get_end().x; x++) { |
2443 | for (int y = rect.get_position().y; y <= rect.get_end().y; y++) { |
2444 | Vector2i coords = Vector2i(x, y); |
2445 | coords = p_tile_set_atlas_source->get_tile_at_coords(coords); |
2446 | if (coords != TileSetSource::INVALID_ATLAS_COORDS) { |
2447 | TileData *tile_data = p_tile_set_atlas_source->get_tile_data(coords, 0); |
2448 | if (tile_data->get_terrain_set() == terrain_set) { |
2449 | TileMapCell cell; |
2450 | cell.source_id = 0; |
2451 | cell.set_atlas_coords(coords); |
2452 | cell.alternative_tile = 0; |
2453 | edited.insert(cell); |
2454 | } |
2455 | } |
2456 | } |
2457 | } |
2458 | |
2459 | Vector<Point2> mouse_pos_rect_polygon; |
2460 | mouse_pos_rect_polygon.push_back(drag_start_pos); |
2461 | mouse_pos_rect_polygon.push_back(Vector2(mb->get_position().x, drag_start_pos.y)); |
2462 | mouse_pos_rect_polygon.push_back(mb->get_position()); |
2463 | mouse_pos_rect_polygon.push_back(Vector2(drag_start_pos.x, mb->get_position().y)); |
2464 | |
2465 | undo_redo->create_action(TTR("Painting Terrain" )); |
2466 | for (const TileMapCell &E : edited) { |
2467 | Vector2i coords = E.get_atlas_coords(); |
2468 | TileData *tile_data = p_tile_set_atlas_source->get_tile_data(coords, 0); |
2469 | |
2470 | Rect2i texture_region = p_tile_set_atlas_source->get_tile_texture_region(coords); |
2471 | Vector2i position = texture_region.get_center() + tile_data->get_texture_origin(); |
2472 | |
2473 | Vector<Vector2> polygon = tile_set->get_terrain_polygon(terrain_set); |
2474 | for (int j = 0; j < polygon.size(); j++) { |
2475 | polygon.write[j] += position; |
2476 | } |
2477 | if (!Geometry2D::intersect_polygons(polygon, mouse_pos_rect_polygon).is_empty()) { |
2478 | // Draw terrain. |
2479 | undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain" , coords.x, coords.y, E.alternative_tile), terrain); |
2480 | undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain" , coords.x, coords.y, E.alternative_tile), tile_data->get_terrain()); |
2481 | } |
2482 | |
2483 | for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { |
2484 | TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); |
2485 | if (tile_set->is_valid_terrain_peering_bit(terrain_set, bit)) { |
2486 | polygon = tile_set->get_terrain_peering_bit_polygon(terrain_set, bit); |
2487 | for (int j = 0; j < polygon.size(); j++) { |
2488 | polygon.write[j] += position; |
2489 | } |
2490 | if (!Geometry2D::intersect_polygons(polygon, mouse_pos_rect_polygon).is_empty()) { |
2491 | // Draw bit. |
2492 | undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E.alternative_tile), terrain); |
2493 | undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E.alternative_tile), tile_data->get_terrain_peering_bit(bit)); |
2494 | } |
2495 | } |
2496 | } |
2497 | } |
2498 | undo_redo->commit_action(true); |
2499 | drag_type = DRAG_TYPE_NONE; |
2500 | accept_event(); |
2501 | } |
2502 | } |
2503 | } |
2504 | } |
2505 | } |
2506 | |
2507 | void TileDataTerrainsEditor::forward_painting_alternatives_gui_input(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_set_atlas_source, const Ref<InputEvent> &p_event) { |
2508 | Ref<InputEventMouseMotion> mm = p_event; |
2509 | if (mm.is_valid()) { |
2510 | if (drag_type == DRAG_TYPE_PAINT_TERRAIN_SET) { |
2511 | Vector3i tile = p_tile_atlas_view->get_alternative_tile_at_pos(mm->get_position()); |
2512 | Vector2i coords = Vector2i(tile.x, tile.y); |
2513 | int alternative_tile = tile.z; |
2514 | |
2515 | if (coords != TileSetSource::INVALID_ATLAS_COORDS) { |
2516 | TileMapCell cell; |
2517 | cell.source_id = 0; |
2518 | cell.set_atlas_coords(coords); |
2519 | cell.alternative_tile = alternative_tile; |
2520 | TileData *tile_data = p_tile_set_atlas_source->get_tile_data(coords, alternative_tile); |
2521 | if (!drag_modified.has(cell)) { |
2522 | Dictionary dict; |
2523 | dict["terrain_set" ] = tile_data->get_terrain_set(); |
2524 | dict["terrain" ] = tile_data->get_terrain(); |
2525 | Array array; |
2526 | for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) { |
2527 | TileSet::CellNeighbor bit = TileSet::CellNeighbor(j); |
2528 | array.push_back(tile_data->is_valid_terrain_peering_bit(bit) ? tile_data->get_terrain_peering_bit(bit) : -1); |
2529 | } |
2530 | dict["terrain_peering_bits" ] = array; |
2531 | drag_modified[cell] = dict; |
2532 | } |
2533 | tile_data->set_terrain_set(drag_painted_value); |
2534 | } |
2535 | |
2536 | drag_last_pos = mm->get_position(); |
2537 | accept_event(); |
2538 | } else if (drag_type == DRAG_TYPE_PAINT_TERRAIN_BITS) { |
2539 | Dictionary painted = Dictionary(drag_painted_value); |
2540 | int terrain_set = int(painted["terrain_set" ]); |
2541 | int terrain = int(painted["terrain" ]); |
2542 | |
2543 | Vector3i tile = p_tile_atlas_view->get_alternative_tile_at_pos(mm->get_position()); |
2544 | Vector2i coords = Vector2i(tile.x, tile.y); |
2545 | int alternative_tile = tile.z; |
2546 | |
2547 | if (coords != TileSetSource::INVALID_ATLAS_COORDS) { |
2548 | TileMapCell cell; |
2549 | cell.source_id = 0; |
2550 | cell.set_atlas_coords(coords); |
2551 | cell.alternative_tile = alternative_tile; |
2552 | |
2553 | // Save the old terrain_set and terrains bits. |
2554 | TileData *tile_data = p_tile_set_atlas_source->get_tile_data(coords, alternative_tile); |
2555 | if (tile_data->get_terrain_set() == terrain_set) { |
2556 | if (!drag_modified.has(cell)) { |
2557 | Dictionary dict; |
2558 | dict["terrain_set" ] = tile_data->get_terrain_set(); |
2559 | dict["terrain" ] = tile_data->get_terrain(); |
2560 | Array array; |
2561 | for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) { |
2562 | TileSet::CellNeighbor bit = TileSet::CellNeighbor(j); |
2563 | array.push_back(tile_data->is_valid_terrain_peering_bit(bit) ? tile_data->get_terrain_peering_bit(bit) : -1); |
2564 | } |
2565 | dict["terrain_peering_bits" ] = array; |
2566 | drag_modified[cell] = dict; |
2567 | } |
2568 | |
2569 | // Set the terrains bits. |
2570 | Rect2i texture_region = p_tile_atlas_view->get_alternative_tile_rect(coords, alternative_tile); |
2571 | Vector2i position = texture_region.get_center() + tile_data->get_texture_origin(); |
2572 | |
2573 | Vector<Vector2> polygon = tile_set->get_terrain_polygon(tile_data->get_terrain_set()); |
2574 | if (Geometry2D::is_segment_intersecting_polygon(mm->get_position() - position, drag_last_pos - position, polygon)) { |
2575 | tile_data->set_terrain(terrain); |
2576 | } |
2577 | |
2578 | for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) { |
2579 | TileSet::CellNeighbor bit = TileSet::CellNeighbor(j); |
2580 | if (tile_data->is_valid_terrain_peering_bit(bit)) { |
2581 | polygon = tile_set->get_terrain_peering_bit_polygon(tile_data->get_terrain_set(), bit); |
2582 | if (Geometry2D::is_segment_intersecting_polygon(mm->get_position() - position, drag_last_pos - position, polygon)) { |
2583 | tile_data->set_terrain_peering_bit(bit, terrain); |
2584 | } |
2585 | } |
2586 | } |
2587 | } |
2588 | } |
2589 | drag_last_pos = mm->get_position(); |
2590 | accept_event(); |
2591 | } |
2592 | } |
2593 | |
2594 | Ref<InputEventMouseButton> mb = p_event; |
2595 | if (mb.is_valid()) { |
2596 | if (mb->get_button_index() == MouseButton::LEFT || mb->get_button_index() == MouseButton::RIGHT) { |
2597 | if (mb->is_pressed()) { |
2598 | if (mb->get_button_index() == MouseButton::LEFT && picker_button->is_pressed()) { |
2599 | Vector3i tile = p_tile_atlas_view->get_alternative_tile_at_pos(mb->get_position()); |
2600 | Vector2i coords = Vector2i(tile.x, tile.y); |
2601 | int alternative_tile = tile.z; |
2602 | |
2603 | if (coords != TileSetSource::INVALID_ATLAS_COORDS) { |
2604 | TileData *tile_data = p_tile_set_atlas_source->get_tile_data(coords, alternative_tile); |
2605 | int terrain_set = tile_data->get_terrain_set(); |
2606 | Rect2i texture_region = p_tile_atlas_view->get_alternative_tile_rect(coords, alternative_tile); |
2607 | Vector2i position = texture_region.get_center() + tile_data->get_texture_origin(); |
2608 | dummy_object->set("terrain_set" , terrain_set); |
2609 | dummy_object->set("terrain" , -1); |
2610 | |
2611 | Vector<Vector2> polygon = tile_set->get_terrain_polygon(terrain_set); |
2612 | if (Geometry2D::is_point_in_polygon(mb->get_position() - position, polygon)) { |
2613 | dummy_object->set("terrain" , tile_data->get_terrain()); |
2614 | } |
2615 | |
2616 | for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { |
2617 | TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); |
2618 | if (tile_set->is_valid_terrain_peering_bit(terrain_set, bit)) { |
2619 | polygon = tile_set->get_terrain_peering_bit_polygon(terrain_set, bit); |
2620 | if (Geometry2D::is_point_in_polygon(mb->get_position() - position, polygon)) { |
2621 | dummy_object->set("terrain" , tile_data->get_terrain_peering_bit(bit)); |
2622 | } |
2623 | } |
2624 | } |
2625 | terrain_set_property_editor->update_property(); |
2626 | _update_terrain_selector(); |
2627 | picker_button->set_pressed(false); |
2628 | accept_event(); |
2629 | } |
2630 | } else { |
2631 | int terrain_set = int(dummy_object->get("terrain_set" )); |
2632 | int terrain = int(dummy_object->get("terrain" )); |
2633 | |
2634 | Vector3i tile = p_tile_atlas_view->get_alternative_tile_at_pos(mb->get_position()); |
2635 | Vector2i coords = Vector2i(tile.x, tile.y); |
2636 | int alternative_tile = tile.z; |
2637 | |
2638 | if (coords != TileSetSource::INVALID_ATLAS_COORDS) { |
2639 | TileData *tile_data = p_tile_set_atlas_source->get_tile_data(coords, alternative_tile); |
2640 | |
2641 | if (terrain_set == -1 || !tile_data || tile_data->get_terrain_set() != terrain_set) { |
2642 | // Paint terrain sets. |
2643 | drag_type = DRAG_TYPE_PAINT_TERRAIN_SET; |
2644 | drag_modified.clear(); |
2645 | drag_painted_value = int(dummy_object->get("terrain_set" )); |
2646 | if (coords != TileSetSource::INVALID_ATLAS_COORDS) { |
2647 | TileMapCell cell; |
2648 | cell.source_id = 0; |
2649 | cell.set_atlas_coords(coords); |
2650 | cell.alternative_tile = alternative_tile; |
2651 | Dictionary dict; |
2652 | dict["terrain_set" ] = tile_data->get_terrain_set(); |
2653 | Array array; |
2654 | for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { |
2655 | TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); |
2656 | array.push_back(tile_data->is_valid_terrain_peering_bit(bit) ? tile_data->get_terrain_peering_bit(bit) : -1); |
2657 | } |
2658 | dict["terrain_peering_bits" ] = array; |
2659 | drag_modified[cell] = dict; |
2660 | tile_data->set_terrain_set(drag_painted_value); |
2661 | } |
2662 | drag_last_pos = mb->get_position(); |
2663 | accept_event(); |
2664 | } else if (tile_data->get_terrain_set() == terrain_set) { |
2665 | // Paint terrain bits. |
2666 | if (mb->get_button_index() == MouseButton::RIGHT) { |
2667 | terrain = -1; |
2668 | } |
2669 | // Paint terrain bits. |
2670 | drag_type = DRAG_TYPE_PAINT_TERRAIN_BITS; |
2671 | drag_modified.clear(); |
2672 | Dictionary painted_dict; |
2673 | painted_dict["terrain_set" ] = terrain_set; |
2674 | painted_dict["terrain" ] = terrain; |
2675 | drag_painted_value = painted_dict; |
2676 | |
2677 | if (coords != TileSetSource::INVALID_ATLAS_COORDS) { |
2678 | TileMapCell cell; |
2679 | cell.source_id = 0; |
2680 | cell.set_atlas_coords(coords); |
2681 | cell.alternative_tile = alternative_tile; |
2682 | |
2683 | // Save the old terrain_set and terrains bits. |
2684 | Dictionary dict; |
2685 | dict["terrain_set" ] = tile_data->get_terrain_set(); |
2686 | dict["terrain" ] = tile_data->get_terrain(); |
2687 | Array array; |
2688 | for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { |
2689 | TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); |
2690 | array.push_back(tile_data->is_valid_terrain_peering_bit(bit) ? tile_data->get_terrain_peering_bit(bit) : -1); |
2691 | } |
2692 | dict["terrain_peering_bits" ] = array; |
2693 | drag_modified[cell] = dict; |
2694 | |
2695 | // Set the terrain bit. |
2696 | Rect2i texture_region = p_tile_atlas_view->get_alternative_tile_rect(coords, alternative_tile); |
2697 | Vector2i position = texture_region.get_center() + tile_data->get_texture_origin(); |
2698 | |
2699 | Vector<Vector2> polygon = tile_set->get_terrain_polygon(terrain_set); |
2700 | if (Geometry2D::is_point_in_polygon(mb->get_position() - position, polygon)) { |
2701 | tile_data->set_terrain(terrain); |
2702 | } |
2703 | for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { |
2704 | TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); |
2705 | if (tile_set->is_valid_terrain_peering_bit(terrain_set, bit)) { |
2706 | polygon = tile_set->get_terrain_peering_bit_polygon(terrain_set, bit); |
2707 | if (Geometry2D::is_point_in_polygon(mb->get_position() - position, polygon)) { |
2708 | tile_data->set_terrain_peering_bit(bit, terrain); |
2709 | } |
2710 | } |
2711 | } |
2712 | } |
2713 | drag_last_pos = mb->get_position(); |
2714 | accept_event(); |
2715 | } |
2716 | } |
2717 | } |
2718 | } else { |
2719 | EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); |
2720 | if (drag_type == DRAG_TYPE_PAINT_TERRAIN_SET) { |
2721 | undo_redo->create_action(TTR("Painting Tiles Property" )); |
2722 | for (KeyValue<TileMapCell, Variant> &E : drag_modified) { |
2723 | Dictionary dict = E.value; |
2724 | Vector2i coords = E.key.get_atlas_coords(); |
2725 | undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain_set" , coords.x, coords.y, E.key.alternative_tile), drag_painted_value); |
2726 | undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain_set" , coords.x, coords.y, E.key.alternative_tile), dict["terrain_set" ]); |
2727 | if (int(dict["terrain_set" ]) >= 0) { |
2728 | undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain" , coords.x, coords.y, E.key.alternative_tile), dict["terrain" ]); |
2729 | Array array = dict["terrain_peering_bits" ]; |
2730 | for (int i = 0; i < array.size(); i++) { |
2731 | undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E.key.alternative_tile), array[i]); |
2732 | } |
2733 | } |
2734 | } |
2735 | undo_redo->commit_action(false); |
2736 | drag_type = DRAG_TYPE_NONE; |
2737 | accept_event(); |
2738 | } else if (drag_type == DRAG_TYPE_PAINT_TERRAIN_BITS) { |
2739 | Dictionary painted = Dictionary(drag_painted_value); |
2740 | int terrain_set = int(painted["terrain_set" ]); |
2741 | int terrain = int(painted["terrain" ]); |
2742 | undo_redo->create_action(TTR("Painting Terrain" )); |
2743 | for (KeyValue<TileMapCell, Variant> &E : drag_modified) { |
2744 | Dictionary dict = E.value; |
2745 | Vector2i coords = E.key.get_atlas_coords(); |
2746 | undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain" , coords.x, coords.y, E.key.alternative_tile), terrain); |
2747 | undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain" , coords.x, coords.y, E.key.alternative_tile), dict["terrain" ]); |
2748 | Array array = dict["terrain_peering_bits" ]; |
2749 | for (int i = 0; i < array.size(); i++) { |
2750 | TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); |
2751 | if (tile_set->is_valid_terrain_peering_bit(terrain_set, bit)) { |
2752 | undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E.key.alternative_tile), terrain); |
2753 | } |
2754 | if (tile_set->is_valid_terrain_peering_bit(dict["terrain_set" ], bit)) { |
2755 | undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E.key.alternative_tile), array[i]); |
2756 | } |
2757 | } |
2758 | } |
2759 | undo_redo->commit_action(false); |
2760 | drag_type = DRAG_TYPE_NONE; |
2761 | accept_event(); |
2762 | } |
2763 | } |
2764 | } |
2765 | } |
2766 | } |
2767 | |
2768 | void TileDataTerrainsEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected) { |
2769 | TileData *tile_data = _get_tile_data(p_cell); |
2770 | ERR_FAIL_NULL(tile_data); |
2771 | |
2772 | tile_set->draw_terrains(p_canvas_item, p_transform, tile_data); |
2773 | } |
2774 | |
2775 | void TileDataTerrainsEditor::_notification(int p_what) { |
2776 | switch (p_what) { |
2777 | case NOTIFICATION_ENTER_TREE: |
2778 | case NOTIFICATION_THEME_CHANGED: { |
2779 | picker_button->set_icon(get_editor_theme_icon(SNAME("ColorPick" ))); |
2780 | } break; |
2781 | } |
2782 | } |
2783 | |
2784 | TileDataTerrainsEditor::TileDataTerrainsEditor() { |
2785 | label = memnew(Label); |
2786 | label->set_text(TTR("Painting:" )); |
2787 | label->set_theme_type_variation("HeaderSmall" ); |
2788 | add_child(label); |
2789 | |
2790 | // Toolbar |
2791 | picker_button = memnew(Button); |
2792 | picker_button->set_flat(true); |
2793 | picker_button->set_toggle_mode(true); |
2794 | picker_button->set_shortcut(ED_SHORTCUT("tiles_editor/picker" , TTR("Picker" ), Key::P)); |
2795 | toolbar->add_child(picker_button); |
2796 | |
2797 | // Setup |
2798 | dummy_object->add_dummy_property("terrain_set" ); |
2799 | dummy_object->set("terrain_set" , -1); |
2800 | dummy_object->add_dummy_property("terrain" ); |
2801 | dummy_object->set("terrain" , -1); |
2802 | |
2803 | // Get the default value for the type. |
2804 | terrain_set_property_editor = memnew(EditorPropertyEnum); |
2805 | terrain_set_property_editor->set_object_and_property(dummy_object, "terrain_set" ); |
2806 | terrain_set_property_editor->set_label("Terrain Set" ); |
2807 | terrain_set_property_editor->connect("property_changed" , callable_mp(this, &TileDataTerrainsEditor::_property_value_changed).unbind(1)); |
2808 | terrain_set_property_editor->set_tooltip_text(terrain_set_property_editor->get_edited_property()); |
2809 | add_child(terrain_set_property_editor); |
2810 | |
2811 | terrain_property_editor = memnew(EditorPropertyEnum); |
2812 | terrain_property_editor->set_object_and_property(dummy_object, "terrain" ); |
2813 | terrain_property_editor->set_label("Terrain" ); |
2814 | terrain_property_editor->connect("property_changed" , callable_mp(this, &TileDataTerrainsEditor::_property_value_changed).unbind(1)); |
2815 | add_child(terrain_property_editor); |
2816 | } |
2817 | |
2818 | TileDataTerrainsEditor::~TileDataTerrainsEditor() { |
2819 | toolbar->queue_free(); |
2820 | memdelete(dummy_object); |
2821 | } |
2822 | |
2823 | Variant TileDataNavigationEditor::_get_painted_value() { |
2824 | Ref<NavigationPolygon> nav_polygon; |
2825 | nav_polygon.instantiate(); |
2826 | |
2827 | for (int i = 0; i < polygon_editor->get_polygon_count(); i++) { |
2828 | Vector<Vector2> polygon = polygon_editor->get_polygon(i); |
2829 | nav_polygon->add_outline(polygon); |
2830 | } |
2831 | |
2832 | nav_polygon->make_polygons_from_outlines(); |
2833 | return nav_polygon; |
2834 | } |
2835 | |
2836 | void TileDataNavigationEditor::_set_painted_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) { |
2837 | TileData *tile_data = p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile); |
2838 | ERR_FAIL_NULL(tile_data); |
2839 | |
2840 | Ref<NavigationPolygon> nav_polygon = tile_data->get_navigation_polygon(navigation_layer); |
2841 | polygon_editor->clear_polygons(); |
2842 | if (nav_polygon.is_valid()) { |
2843 | for (int i = 0; i < nav_polygon->get_outline_count(); i++) { |
2844 | polygon_editor->add_polygon(nav_polygon->get_outline(i)); |
2845 | } |
2846 | } |
2847 | polygon_editor->set_background(p_tile_set_atlas_source->get_texture(), p_tile_set_atlas_source->get_tile_texture_region(p_coords), tile_data->get_texture_origin(), tile_data->get_flip_h(), tile_data->get_flip_v(), tile_data->get_transpose(), tile_data->get_modulate()); |
2848 | } |
2849 | |
2850 | void TileDataNavigationEditor::_set_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile, Variant p_value) { |
2851 | TileData *tile_data = p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile); |
2852 | ERR_FAIL_NULL(tile_data); |
2853 | Ref<NavigationPolygon> nav_polygon = p_value; |
2854 | tile_data->set_navigation_polygon(navigation_layer, nav_polygon); |
2855 | |
2856 | polygon_editor->set_background(p_tile_set_atlas_source->get_texture(), p_tile_set_atlas_source->get_tile_texture_region(p_coords), tile_data->get_texture_origin(), tile_data->get_flip_h(), tile_data->get_flip_v(), tile_data->get_transpose(), tile_data->get_modulate()); |
2857 | } |
2858 | |
2859 | Variant TileDataNavigationEditor::_get_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) { |
2860 | TileData *tile_data = p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile); |
2861 | ERR_FAIL_NULL_V(tile_data, Variant()); |
2862 | return tile_data->get_navigation_polygon(navigation_layer); |
2863 | } |
2864 | |
2865 | void TileDataNavigationEditor::_setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, HashMap<TileMapCell, Variant, TileMapCell> p_previous_values, Variant p_new_value) { |
2866 | EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); |
2867 | for (const KeyValue<TileMapCell, Variant> &E : p_previous_values) { |
2868 | Vector2i coords = E.key.get_atlas_coords(); |
2869 | undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/navigation_layer_%d/polygon" , coords.x, coords.y, E.key.alternative_tile, navigation_layer), E.value); |
2870 | undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/navigation_layer_%d/polygon" , coords.x, coords.y, E.key.alternative_tile, navigation_layer), p_new_value); |
2871 | } |
2872 | } |
2873 | |
2874 | void TileDataNavigationEditor::_tile_set_changed() { |
2875 | polygon_editor->set_tile_set(tile_set); |
2876 | } |
2877 | |
2878 | void TileDataNavigationEditor::_notification(int p_what) { |
2879 | switch (p_what) { |
2880 | case NOTIFICATION_ENTER_TREE: { |
2881 | #ifdef DEBUG_ENABLED |
2882 | polygon_editor->set_polygons_color(NavigationServer3D::get_singleton()->get_debug_navigation_geometry_face_color()); |
2883 | #endif // DEBUG_ENABLED |
2884 | } break; |
2885 | } |
2886 | } |
2887 | |
2888 | TileDataNavigationEditor::TileDataNavigationEditor() { |
2889 | polygon_editor = memnew(GenericTilePolygonEditor); |
2890 | polygon_editor->set_multiple_polygon_mode(true); |
2891 | add_child(polygon_editor); |
2892 | } |
2893 | |
2894 | void TileDataNavigationEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected) { |
2895 | TileData *tile_data = _get_tile_data(p_cell); |
2896 | ERR_FAIL_NULL(tile_data); |
2897 | |
2898 | // Draw all shapes. |
2899 | RenderingServer::get_singleton()->canvas_item_add_set_transform(p_canvas_item->get_canvas_item(), p_transform); |
2900 | |
2901 | Ref<NavigationPolygon> nav_polygon = tile_data->get_navigation_polygon(navigation_layer); |
2902 | if (nav_polygon.is_valid()) { |
2903 | Vector<Vector2> verts = nav_polygon->get_vertices(); |
2904 | if (verts.size() < 3) { |
2905 | return; |
2906 | } |
2907 | |
2908 | Color color = Color(0.5, 1.0, 1.0, 1.0); |
2909 | #ifdef DEBUG_ENABLED |
2910 | color = NavigationServer3D::get_singleton()->get_debug_navigation_geometry_face_color(); |
2911 | #endif // DEBUG_ENABLED |
2912 | if (p_selected) { |
2913 | Color grid_color = EDITOR_GET("editors/tiles_editor/grid_color" ); |
2914 | 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); |
2915 | selection_color.a = 0.7; |
2916 | color = selection_color; |
2917 | } |
2918 | |
2919 | RandomPCG rand; |
2920 | for (int i = 0; i < nav_polygon->get_polygon_count(); i++) { |
2921 | // An array of vertices for this polygon. |
2922 | Vector<int> polygon = nav_polygon->get_polygon(i); |
2923 | Vector<Vector2> vertices; |
2924 | vertices.resize(polygon.size()); |
2925 | for (int j = 0; j < polygon.size(); j++) { |
2926 | ERR_FAIL_INDEX(polygon[j], verts.size()); |
2927 | vertices.write[j] = verts[polygon[j]]; |
2928 | } |
2929 | |
2930 | // Generate the polygon color, slightly randomly modified from the settings one. |
2931 | Color random_variation_color; |
2932 | random_variation_color.set_hsv(color.get_h() + rand.random(-1.0, 1.0) * 0.05, color.get_s(), color.get_v() + rand.random(-1.0, 1.0) * 0.1); |
2933 | random_variation_color.a = color.a; |
2934 | Vector<Color> colors; |
2935 | colors.push_back(random_variation_color); |
2936 | |
2937 | RenderingServer::get_singleton()->canvas_item_add_polygon(p_canvas_item->get_canvas_item(), vertices, colors); |
2938 | } |
2939 | } |
2940 | |
2941 | RenderingServer::get_singleton()->canvas_item_add_set_transform(p_canvas_item->get_canvas_item(), Transform2D()); |
2942 | } |
2943 | |