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