1 /*************************************************************************/
2 /* tile_set_editor_plugin.cpp */
3 /*************************************************************************/
4 /* This file is part of: */
5 /* GODOT ENGINE */
6 /* https://godotengine.org */
7 /*************************************************************************/
8 /* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
9 /* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
10 /* */
11 /* Permission is hereby granted, free of charge, to any person obtaining */
12 /* a copy of this software and associated documentation files (the */
13 /* "Software"), to deal in the Software without restriction, including */
14 /* without limitation the rights to use, copy, modify, merge, publish, */
15 /* distribute, sublicense, and/or sell copies of the Software, and to */
16 /* permit persons to whom the Software is furnished to do so, subject to */
17 /* the following conditions: */
18 /* */
19 /* The above copyright notice and this permission notice shall be */
20 /* included in all copies or substantial portions of the Software. */
21 /* */
22 /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23 /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24 /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
25 /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26 /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27 /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29 /*************************************************************************/
30
31 #include "tile_set_editor_plugin.h"
32
33 #include "core/os/input.h"
34 #include "core/os/keyboard.h"
35 #include "editor/editor_scale.h"
36 #include "editor/plugins/canvas_item_editor_plugin.h"
37 #include "scene/2d/physics_body_2d.h"
38 #include "scene/2d/sprite.h"
39
edit(const Ref<TileSet> & p_tileset)40 void TileSetEditor::edit(const Ref<TileSet> &p_tileset) {
41
42 tileset = p_tileset;
43 tileset->add_change_receptor(this);
44
45 texture_list->clear();
46 texture_map.clear();
47 update_texture_list();
48 }
49
_import_node(Node * p_node,Ref<TileSet> p_library)50 void TileSetEditor::_import_node(Node *p_node, Ref<TileSet> p_library) {
51
52 for (int i = 0; i < p_node->get_child_count(); i++) {
53
54 Node *child = p_node->get_child(i);
55
56 if (!Object::cast_to<Sprite>(child)) {
57 if (child->get_child_count() > 0) {
58 _import_node(child, p_library);
59 }
60
61 continue;
62 }
63
64 Sprite *mi = Object::cast_to<Sprite>(child);
65 Ref<Texture> texture = mi->get_texture();
66 Ref<Texture> normal_map = mi->get_normal_map();
67 Ref<ShaderMaterial> material = mi->get_material();
68
69 if (texture.is_null())
70 continue;
71
72 int id = p_library->find_tile_by_name(mi->get_name());
73 if (id < 0) {
74
75 id = p_library->get_last_unused_tile_id();
76 p_library->create_tile(id);
77 p_library->tile_set_name(id, mi->get_name());
78 }
79
80 p_library->tile_set_texture(id, texture);
81 p_library->tile_set_normal_map(id, normal_map);
82 p_library->tile_set_material(id, material);
83
84 p_library->tile_set_modulate(id, mi->get_modulate());
85
86 Vector2 phys_offset;
87 Size2 s;
88
89 if (mi->is_region()) {
90 s = mi->get_region_rect().size;
91 p_library->tile_set_region(id, mi->get_region_rect());
92 } else {
93 const int frame = mi->get_frame();
94 const int hframes = mi->get_hframes();
95 s = texture->get_size() / Size2(hframes, mi->get_vframes());
96 p_library->tile_set_region(id, Rect2(Vector2(frame % hframes, frame / hframes) * s, s));
97 }
98
99 if (mi->is_centered()) {
100 phys_offset += -s / 2;
101 }
102
103 Vector<TileSet::ShapeData> collisions;
104 Ref<NavigationPolygon> nav_poly;
105 Ref<OccluderPolygon2D> occluder;
106 bool found_collisions = false;
107
108 for (int j = 0; j < mi->get_child_count(); j++) {
109
110 Node *child2 = mi->get_child(j);
111
112 if (Object::cast_to<NavigationPolygonInstance>(child2))
113 nav_poly = Object::cast_to<NavigationPolygonInstance>(child2)->get_navigation_polygon();
114
115 if (Object::cast_to<LightOccluder2D>(child2))
116 occluder = Object::cast_to<LightOccluder2D>(child2)->get_occluder_polygon();
117
118 if (!Object::cast_to<StaticBody2D>(child2))
119 continue;
120
121 found_collisions = true;
122
123 StaticBody2D *sb = Object::cast_to<StaticBody2D>(child2);
124
125 List<uint32_t> shapes;
126 sb->get_shape_owners(&shapes);
127
128 for (List<uint32_t>::Element *E = shapes.front(); E; E = E->next()) {
129 if (sb->is_shape_owner_disabled(E->get())) continue;
130
131 Transform2D shape_transform = sb->get_transform() * sb->shape_owner_get_transform(E->get());
132 bool one_way = sb->is_shape_owner_one_way_collision_enabled(E->get());
133
134 shape_transform[2] -= phys_offset;
135
136 for (int k = 0; k < sb->shape_owner_get_shape_count(E->get()); k++) {
137
138 Ref<Shape2D> shape = sb->shape_owner_get_shape(E->get(), k);
139 TileSet::ShapeData shape_data;
140 shape_data.shape = shape;
141 shape_data.shape_transform = shape_transform;
142 shape_data.one_way_collision = one_way;
143 collisions.push_back(shape_data);
144 }
145 }
146 }
147
148 if (found_collisions) {
149 p_library->tile_set_shapes(id, collisions);
150 }
151
152 p_library->tile_set_texture_offset(id, mi->get_offset());
153 p_library->tile_set_navigation_polygon(id, nav_poly);
154 p_library->tile_set_light_occluder(id, occluder);
155 p_library->tile_set_occluder_offset(id, -phys_offset);
156 p_library->tile_set_navigation_polygon_offset(id, -phys_offset);
157 p_library->tile_set_z_index(id, mi->get_z_index());
158 }
159 }
160
_import_scene(Node * p_scene,Ref<TileSet> p_library,bool p_merge)161 void TileSetEditor::_import_scene(Node *p_scene, Ref<TileSet> p_library, bool p_merge) {
162
163 if (!p_merge)
164 p_library->clear();
165
166 _import_node(p_scene, p_library);
167 }
168
_undo_redo_import_scene(Node * p_scene,bool p_merge)169 void TileSetEditor::_undo_redo_import_scene(Node *p_scene, bool p_merge) {
170
171 _import_scene(p_scene, tileset, p_merge);
172 }
173
update_library_file(Node * p_base_scene,Ref<TileSet> ml,bool p_merge)174 Error TileSetEditor::update_library_file(Node *p_base_scene, Ref<TileSet> ml, bool p_merge) {
175
176 _import_scene(p_base_scene, ml, p_merge);
177 return OK;
178 }
179
get_drag_data_fw(const Point2 & p_point,Control * p_from)180 Variant TileSetEditor::get_drag_data_fw(const Point2 &p_point, Control *p_from) {
181
182 return false;
183 }
184
can_drop_data_fw(const Point2 & p_point,const Variant & p_data,Control * p_from) const185 bool TileSetEditor::can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const {
186
187 Dictionary d = p_data;
188
189 if (!d.has("type"))
190 return false;
191
192 if (d.has("from") && (Object *)(d["from"]) == texture_list)
193 return false;
194
195 if (String(d["type"]) == "resource" && d.has("resource")) {
196 RES r = d["resource"];
197
198 Ref<Texture> texture = r;
199
200 if (texture.is_valid()) {
201
202 return true;
203 }
204 }
205
206 if (String(d["type"]) == "files") {
207
208 Vector<String> files = d["files"];
209
210 if (files.size() == 0)
211 return false;
212
213 for (int i = 0; i < files.size(); i++) {
214 String file = files[i];
215 String ftype = EditorFileSystem::get_singleton()->get_file_type(file);
216
217 if (!ClassDB::is_parent_class(ftype, "Texture")) {
218 return false;
219 }
220 }
221
222 return true;
223 }
224 return false;
225 }
226
drop_data_fw(const Point2 & p_point,const Variant & p_data,Control * p_from)227 void TileSetEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) {
228
229 if (!can_drop_data_fw(p_point, p_data, p_from))
230 return;
231
232 Dictionary d = p_data;
233
234 if (!d.has("type"))
235 return;
236
237 if (String(d["type"]) == "resource" && d.has("resource")) {
238 RES r = d["resource"];
239
240 Ref<Texture> texture = r;
241
242 if (texture.is_valid())
243 add_texture(texture);
244
245 if (texture_list->get_item_count() > 0) {
246 update_texture_list_icon();
247 texture_list->select(texture_list->get_item_count() - 1);
248 _on_texture_list_selected(texture_list->get_item_count() - 1);
249 }
250 }
251
252 if (String(d["type"]) == "files") {
253
254 PoolVector<String> files = d["files"];
255
256 _on_textures_added(files);
257 }
258 }
259
_bind_methods()260 void TileSetEditor::_bind_methods() {
261
262 ClassDB::bind_method("_undo_redo_import_scene", &TileSetEditor::_undo_redo_import_scene);
263 ClassDB::bind_method("_on_tileset_toolbar_button_pressed", &TileSetEditor::_on_tileset_toolbar_button_pressed);
264 ClassDB::bind_method("_on_textures_added", &TileSetEditor::_on_textures_added);
265 ClassDB::bind_method("_on_tileset_toolbar_confirm", &TileSetEditor::_on_tileset_toolbar_confirm);
266 ClassDB::bind_method("_on_texture_list_selected", &TileSetEditor::_on_texture_list_selected);
267 ClassDB::bind_method("_on_edit_mode_changed", &TileSetEditor::_on_edit_mode_changed);
268 ClassDB::bind_method("_on_scroll_container_input", &TileSetEditor::_on_scroll_container_input);
269 ClassDB::bind_method("_on_workspace_mode_changed", &TileSetEditor::_on_workspace_mode_changed);
270 ClassDB::bind_method("_on_workspace_overlay_draw", &TileSetEditor::_on_workspace_overlay_draw);
271 ClassDB::bind_method("_on_workspace_process", &TileSetEditor::_on_workspace_process);
272 ClassDB::bind_method("_on_workspace_draw", &TileSetEditor::_on_workspace_draw);
273 ClassDB::bind_method("_on_workspace_input", &TileSetEditor::_on_workspace_input);
274 ClassDB::bind_method("_on_tool_clicked", &TileSetEditor::_on_tool_clicked);
275 ClassDB::bind_method("_on_priority_changed", &TileSetEditor::_on_priority_changed);
276 ClassDB::bind_method("_on_z_index_changed", &TileSetEditor::_on_z_index_changed);
277 ClassDB::bind_method("_on_grid_snap_toggled", &TileSetEditor::_on_grid_snap_toggled);
278 ClassDB::bind_method("_set_snap_step", &TileSetEditor::_set_snap_step);
279 ClassDB::bind_method("_set_snap_off", &TileSetEditor::_set_snap_off);
280 ClassDB::bind_method("_set_snap_sep", &TileSetEditor::_set_snap_sep);
281 ClassDB::bind_method("_validate_current_tile_id", &TileSetEditor::_validate_current_tile_id);
282 ClassDB::bind_method("_zoom_in", &TileSetEditor::_zoom_in);
283 ClassDB::bind_method("_zoom_out", &TileSetEditor::_zoom_out);
284 ClassDB::bind_method("_zoom_reset", &TileSetEditor::_zoom_reset);
285 ClassDB::bind_method("_select_edited_shape_coord", &TileSetEditor::_select_edited_shape_coord);
286 ClassDB::bind_method("_sort_tiles", &TileSetEditor::_sort_tiles);
287
288 ClassDB::bind_method(D_METHOD("get_drag_data_fw"), &TileSetEditor::get_drag_data_fw);
289 ClassDB::bind_method(D_METHOD("can_drop_data_fw"), &TileSetEditor::can_drop_data_fw);
290 ClassDB::bind_method(D_METHOD("drop_data_fw"), &TileSetEditor::drop_data_fw);
291
292 ClassDB::bind_method("edit", &TileSetEditor::edit);
293 ClassDB::bind_method("add_texture", &TileSetEditor::add_texture);
294 ClassDB::bind_method("remove_texture", &TileSetEditor::remove_texture);
295 ClassDB::bind_method("update_texture_list_icon", &TileSetEditor::update_texture_list_icon);
296 ClassDB::bind_method("update_workspace_minsize", &TileSetEditor::update_workspace_minsize);
297 }
298
_notification(int p_what)299 void TileSetEditor::_notification(int p_what) {
300
301 switch (p_what) {
302 case NOTIFICATION_READY: {
303
304 add_constant_override("autohide", 1); // Fixes the dragger always showing up.
305 } break;
306 case NOTIFICATION_ENTER_TREE:
307 case NOTIFICATION_THEME_CHANGED: {
308
309 tileset_toolbar_buttons[TOOL_TILESET_ADD_TEXTURE]->set_icon(get_icon("ToolAddNode", "EditorIcons"));
310 tileset_toolbar_buttons[TOOL_TILESET_REMOVE_TEXTURE]->set_icon(get_icon("Remove", "EditorIcons"));
311 tileset_toolbar_tools->set_icon(get_icon("Tools", "EditorIcons"));
312
313 tool_workspacemode[WORKSPACE_EDIT]->set_icon(get_icon("Edit", "EditorIcons"));
314 tool_workspacemode[WORKSPACE_CREATE_SINGLE]->set_icon(get_icon("AddSingleTile", "EditorIcons"));
315 tool_workspacemode[WORKSPACE_CREATE_AUTOTILE]->set_icon(get_icon("AddAutotile", "EditorIcons"));
316 tool_workspacemode[WORKSPACE_CREATE_ATLAS]->set_icon(get_icon("AddAtlasTile", "EditorIcons"));
317
318 tools[TOOL_SELECT]->set_icon(get_icon("ToolSelect", "EditorIcons"));
319 tools[BITMASK_COPY]->set_icon(get_icon("Duplicate", "EditorIcons"));
320 tools[BITMASK_PASTE]->set_icon(get_icon("Override", "EditorIcons"));
321 tools[BITMASK_CLEAR]->set_icon(get_icon("Clear", "EditorIcons"));
322 tools[SHAPE_NEW_POLYGON]->set_icon(get_icon("CollisionPolygon2D", "EditorIcons"));
323 tools[SHAPE_NEW_RECTANGLE]->set_icon(get_icon("CollisionShape2D", "EditorIcons"));
324 tools[SELECT_PREVIOUS]->set_icon(get_icon("ArrowLeft", "EditorIcons"));
325 tools[SELECT_NEXT]->set_icon(get_icon("ArrowRight", "EditorIcons"));
326 tools[SHAPE_DELETE]->set_icon(get_icon("Remove", "EditorIcons"));
327 tools[SHAPE_KEEP_INSIDE_TILE]->set_icon(get_icon("Snap", "EditorIcons"));
328 tools[TOOL_GRID_SNAP]->set_icon(get_icon("SnapGrid", "EditorIcons"));
329 tools[ZOOM_OUT]->set_icon(get_icon("ZoomLess", "EditorIcons"));
330 tools[ZOOM_1]->set_icon(get_icon("ZoomReset", "EditorIcons"));
331 tools[ZOOM_IN]->set_icon(get_icon("ZoomMore", "EditorIcons"));
332 tools[VISIBLE_INFO]->set_icon(get_icon("InformationSign", "EditorIcons"));
333 _update_toggle_shape_button();
334
335 tool_editmode[EDITMODE_REGION]->set_icon(get_icon("RegionEdit", "EditorIcons"));
336 tool_editmode[EDITMODE_COLLISION]->set_icon(get_icon("StaticBody2D", "EditorIcons"));
337 tool_editmode[EDITMODE_OCCLUSION]->set_icon(get_icon("LightOccluder2D", "EditorIcons"));
338 tool_editmode[EDITMODE_NAVIGATION]->set_icon(get_icon("Navigation2D", "EditorIcons"));
339 tool_editmode[EDITMODE_BITMASK]->set_icon(get_icon("PackedDataContainer", "EditorIcons"));
340 tool_editmode[EDITMODE_PRIORITY]->set_icon(get_icon("MaterialPreviewLight1", "EditorIcons"));
341 tool_editmode[EDITMODE_ICON]->set_icon(get_icon("LargeTexture", "EditorIcons"));
342 tool_editmode[EDITMODE_Z_INDEX]->set_icon(get_icon("Sort", "EditorIcons"));
343
344 scroll->add_style_override("bg", get_stylebox("bg", "Tree"));
345 } break;
346 }
347 }
348
TileSetEditor(EditorNode * p_editor)349 TileSetEditor::TileSetEditor(EditorNode *p_editor) {
350
351 editor = p_editor;
352 undo_redo = EditorNode::get_undo_redo();
353 current_tile = -1;
354
355 VBoxContainer *left_container = memnew(VBoxContainer);
356 add_child(left_container);
357
358 texture_list = memnew(ItemList);
359 left_container->add_child(texture_list);
360 texture_list->set_v_size_flags(SIZE_EXPAND_FILL);
361 texture_list->set_custom_minimum_size(Size2(200, 0));
362 texture_list->connect("item_selected", this, "_on_texture_list_selected");
363 texture_list->set_drag_forwarding(this);
364
365 HBoxContainer *tileset_toolbar_container = memnew(HBoxContainer);
366 left_container->add_child(tileset_toolbar_container);
367
368 tileset_toolbar_buttons[TOOL_TILESET_ADD_TEXTURE] = memnew(ToolButton);
369 tileset_toolbar_buttons[TOOL_TILESET_ADD_TEXTURE]->connect("pressed", this, "_on_tileset_toolbar_button_pressed", varray(TOOL_TILESET_ADD_TEXTURE));
370 tileset_toolbar_container->add_child(tileset_toolbar_buttons[TOOL_TILESET_ADD_TEXTURE]);
371 tileset_toolbar_buttons[TOOL_TILESET_ADD_TEXTURE]->set_tooltip(TTR("Add Texture(s) to TileSet."));
372
373 tileset_toolbar_buttons[TOOL_TILESET_REMOVE_TEXTURE] = memnew(ToolButton);
374 tileset_toolbar_buttons[TOOL_TILESET_REMOVE_TEXTURE]->connect("pressed", this, "_on_tileset_toolbar_button_pressed", varray(TOOL_TILESET_REMOVE_TEXTURE));
375 tileset_toolbar_container->add_child(tileset_toolbar_buttons[TOOL_TILESET_REMOVE_TEXTURE]);
376 tileset_toolbar_buttons[TOOL_TILESET_REMOVE_TEXTURE]->set_tooltip(TTR("Remove selected Texture from TileSet."));
377
378 Control *toolbar_separator = memnew(Control);
379 toolbar_separator->set_h_size_flags(Control::SIZE_EXPAND_FILL);
380 tileset_toolbar_container->add_child(toolbar_separator);
381
382 tileset_toolbar_tools = memnew(MenuButton);
383 tileset_toolbar_tools->set_text(TTR("Tools"));
384 tileset_toolbar_tools->get_popup()->add_item(TTR("Create from Scene"), TOOL_TILESET_CREATE_SCENE);
385 tileset_toolbar_tools->get_popup()->add_item(TTR("Merge from Scene"), TOOL_TILESET_MERGE_SCENE);
386
387 tileset_toolbar_tools->get_popup()->connect("id_pressed", this, "_on_tileset_toolbar_button_pressed");
388 tileset_toolbar_container->add_child(tileset_toolbar_tools);
389
390 //---------------
391 VBoxContainer *right_container = memnew(VBoxContainer);
392 right_container->set_v_size_flags(SIZE_EXPAND_FILL);
393 add_child(right_container);
394
395 dragging_point = -1;
396 creating_shape = false;
397 snap_step = Vector2(32, 32);
398 snap_offset = WORKSPACE_MARGIN;
399
400 set_custom_minimum_size(Size2(0, 150));
401
402 VBoxContainer *main_vb = memnew(VBoxContainer);
403 right_container->add_child(main_vb);
404 main_vb->set_v_size_flags(SIZE_EXPAND_FILL);
405
406 HBoxContainer *tool_hb = memnew(HBoxContainer);
407 Ref<ButtonGroup> g(memnew(ButtonGroup));
408
409 String workspace_label[WORKSPACE_MODE_MAX] = {
410 TTR("Edit"),
411 TTR("New Single Tile"),
412 TTR("New Autotile"),
413 TTR("New Atlas")
414 };
415 for (int i = 0; i < (int)WORKSPACE_MODE_MAX; i++) {
416 tool_workspacemode[i] = memnew(Button);
417 tool_workspacemode[i]->set_text(workspace_label[i]);
418 tool_workspacemode[i]->set_toggle_mode(true);
419 tool_workspacemode[i]->set_button_group(g);
420 tool_workspacemode[i]->connect("pressed", this, "_on_workspace_mode_changed", varray(i));
421 tool_hb->add_child(tool_workspacemode[i]);
422 }
423
424 Control *spacer = memnew(Control);
425 spacer->set_h_size_flags(Control::SIZE_EXPAND_FILL);
426 tool_hb->add_child(spacer);
427 tool_hb->move_child(spacer, WORKSPACE_CREATE_SINGLE);
428
429 tools[SELECT_NEXT] = memnew(ToolButton);
430 tool_hb->add_child(tools[SELECT_NEXT]);
431 tool_hb->move_child(tools[SELECT_NEXT], WORKSPACE_CREATE_SINGLE);
432 tools[SELECT_NEXT]->set_shortcut(ED_SHORTCUT("tileset_editor/next_shape", TTR("Next Coordinate"), KEY_PAGEDOWN));
433 tools[SELECT_NEXT]->connect("pressed", this, "_on_tool_clicked", varray(SELECT_NEXT));
434 tools[SELECT_NEXT]->set_tooltip(TTR("Select the next shape, subtile, or Tile."));
435 tools[SELECT_PREVIOUS] = memnew(ToolButton);
436 tool_hb->add_child(tools[SELECT_PREVIOUS]);
437 tool_hb->move_child(tools[SELECT_PREVIOUS], WORKSPACE_CREATE_SINGLE);
438 tools[SELECT_PREVIOUS]->set_shortcut(ED_SHORTCUT("tileset_editor/previous_shape", TTR("Previous Coordinate"), KEY_PAGEUP));
439 tools[SELECT_PREVIOUS]->set_tooltip(TTR("Select the previous shape, subtile, or Tile."));
440 tools[SELECT_PREVIOUS]->connect("pressed", this, "_on_tool_clicked", varray(SELECT_PREVIOUS));
441
442 VSeparator *separator_shape_selection = memnew(VSeparator);
443 tool_hb->add_child(separator_shape_selection);
444 tool_hb->move_child(separator_shape_selection, WORKSPACE_CREATE_SINGLE);
445
446 tool_workspacemode[WORKSPACE_EDIT]->set_pressed(true);
447 workspace_mode = WORKSPACE_EDIT;
448
449 main_vb->add_child(tool_hb);
450 main_vb->add_child(memnew(HSeparator));
451
452 tool_hb = memnew(HBoxContainer);
453
454 g = Ref<ButtonGroup>(memnew(ButtonGroup));
455 String label[EDITMODE_MAX] = {
456 TTR("Region"),
457 TTR("Collision"),
458 TTR("Occlusion"),
459 TTR("Navigation"),
460 TTR("Bitmask"),
461 TTR("Priority"),
462 TTR("Icon"),
463 TTR("Z Index")
464 };
465 for (int i = 0; i < (int)EDITMODE_MAX; i++) {
466 tool_editmode[i] = memnew(Button);
467 tool_editmode[i]->set_text(label[i]);
468 tool_editmode[i]->set_toggle_mode(true);
469 tool_editmode[i]->set_button_group(g);
470 tool_editmode[i]->connect("pressed", this, "_on_edit_mode_changed", varray(i));
471 tool_hb->add_child(tool_editmode[i]);
472 }
473 tool_editmode[EDITMODE_COLLISION]->set_pressed(true);
474 edit_mode = EDITMODE_COLLISION;
475
476 tool_editmode[EDITMODE_REGION]->set_shortcut(ED_SHORTCUT("tileset_editor/editmode_region", TTR("Region Mode"), KEY_1));
477 tool_editmode[EDITMODE_COLLISION]->set_shortcut(ED_SHORTCUT("tileset_editor/editmode_collision", TTR("Collision Mode"), KEY_2));
478 tool_editmode[EDITMODE_OCCLUSION]->set_shortcut(ED_SHORTCUT("tileset_editor/editmode_occlusion", TTR("Occlusion Mode"), KEY_3));
479 tool_editmode[EDITMODE_NAVIGATION]->set_shortcut(ED_SHORTCUT("tileset_editor/editmode_navigation", TTR("Navigation Mode"), KEY_4));
480 tool_editmode[EDITMODE_BITMASK]->set_shortcut(ED_SHORTCUT("tileset_editor/editmode_bitmask", TTR("Bitmask Mode"), KEY_5));
481 tool_editmode[EDITMODE_PRIORITY]->set_shortcut(ED_SHORTCUT("tileset_editor/editmode_priority", TTR("Priority Mode"), KEY_6));
482 tool_editmode[EDITMODE_ICON]->set_shortcut(ED_SHORTCUT("tileset_editor/editmode_icon", TTR("Icon Mode"), KEY_7));
483 tool_editmode[EDITMODE_Z_INDEX]->set_shortcut(ED_SHORTCUT("tileset_editor/editmode_z_index", TTR("Z Index Mode"), KEY_8));
484
485 main_vb->add_child(tool_hb);
486 separator_editmode = memnew(HSeparator);
487 main_vb->add_child(separator_editmode);
488
489 toolbar = memnew(HBoxContainer);
490 Ref<ButtonGroup> tg(memnew(ButtonGroup));
491
492 tools[TOOL_SELECT] = memnew(ToolButton);
493 toolbar->add_child(tools[TOOL_SELECT]);
494 tools[TOOL_SELECT]->set_toggle_mode(true);
495 tools[TOOL_SELECT]->set_button_group(tg);
496 tools[TOOL_SELECT]->set_pressed(true);
497 tools[TOOL_SELECT]->connect("pressed", this, "_on_tool_clicked", varray(TOOL_SELECT));
498
499 separator_bitmask = memnew(VSeparator);
500 toolbar->add_child(separator_bitmask);
501 tools[BITMASK_COPY] = memnew(ToolButton);
502 tools[BITMASK_COPY]->set_tooltip(TTR("Copy bitmask."));
503 tools[BITMASK_COPY]->connect("pressed", this, "_on_tool_clicked", varray(BITMASK_COPY));
504 toolbar->add_child(tools[BITMASK_COPY]);
505 tools[BITMASK_PASTE] = memnew(ToolButton);
506 tools[BITMASK_PASTE]->set_tooltip(TTR("Paste bitmask."));
507 tools[BITMASK_PASTE]->connect("pressed", this, "_on_tool_clicked", varray(BITMASK_PASTE));
508 toolbar->add_child(tools[BITMASK_PASTE]);
509 tools[BITMASK_CLEAR] = memnew(ToolButton);
510 tools[BITMASK_CLEAR]->set_tooltip(TTR("Erase bitmask."));
511 tools[BITMASK_CLEAR]->connect("pressed", this, "_on_tool_clicked", varray(BITMASK_CLEAR));
512 toolbar->add_child(tools[BITMASK_CLEAR]);
513
514 tools[SHAPE_NEW_RECTANGLE] = memnew(ToolButton);
515 toolbar->add_child(tools[SHAPE_NEW_RECTANGLE]);
516 tools[SHAPE_NEW_RECTANGLE]->set_toggle_mode(true);
517 tools[SHAPE_NEW_RECTANGLE]->set_button_group(tg);
518 tools[SHAPE_NEW_RECTANGLE]->set_tooltip(TTR("Create a new rectangle."));
519 tools[SHAPE_NEW_RECTANGLE]->connect("pressed", this, "_on_tool_clicked", varray(SHAPE_NEW_RECTANGLE));
520
521 tools[SHAPE_NEW_POLYGON] = memnew(ToolButton);
522 toolbar->add_child(tools[SHAPE_NEW_POLYGON]);
523 tools[SHAPE_NEW_POLYGON]->set_toggle_mode(true);
524 tools[SHAPE_NEW_POLYGON]->set_button_group(tg);
525 tools[SHAPE_NEW_POLYGON]->set_tooltip(TTR("Create a new polygon."));
526 tools[SHAPE_NEW_POLYGON]->connect("pressed", this, "_on_tool_clicked", varray(SHAPE_NEW_POLYGON));
527
528 separator_shape_toggle = memnew(VSeparator);
529 toolbar->add_child(separator_shape_toggle);
530 tools[SHAPE_TOGGLE_TYPE] = memnew(ToolButton);
531 tools[SHAPE_TOGGLE_TYPE]->connect("pressed", this, "_on_tool_clicked", varray(SHAPE_TOGGLE_TYPE));
532 toolbar->add_child(tools[SHAPE_TOGGLE_TYPE]);
533
534 separator_delete = memnew(VSeparator);
535 toolbar->add_child(separator_delete);
536 tools[SHAPE_DELETE] = memnew(ToolButton);
537 tools[SHAPE_DELETE]->connect("pressed", this, "_on_tool_clicked", varray(SHAPE_DELETE));
538 toolbar->add_child(tools[SHAPE_DELETE]);
539
540 spin_priority = memnew(SpinBox);
541 spin_priority->set_min(1);
542 spin_priority->set_max(255);
543 spin_priority->set_step(1);
544 spin_priority->set_custom_minimum_size(Size2(100, 0));
545 spin_priority->connect("value_changed", this, "_on_priority_changed");
546 spin_priority->hide();
547 toolbar->add_child(spin_priority);
548
549 spin_z_index = memnew(SpinBox);
550 spin_z_index->set_min(VS::CANVAS_ITEM_Z_MIN);
551 spin_z_index->set_max(VS::CANVAS_ITEM_Z_MAX);
552 spin_z_index->set_step(1);
553 spin_z_index->set_custom_minimum_size(Size2(100, 0));
554 spin_z_index->connect("value_changed", this, "_on_z_index_changed");
555 spin_z_index->hide();
556 toolbar->add_child(spin_z_index);
557
558 separator_grid = memnew(VSeparator);
559 toolbar->add_child(separator_grid);
560 tools[SHAPE_KEEP_INSIDE_TILE] = memnew(ToolButton);
561 tools[SHAPE_KEEP_INSIDE_TILE]->set_toggle_mode(true);
562 tools[SHAPE_KEEP_INSIDE_TILE]->set_pressed(true);
563 tools[SHAPE_KEEP_INSIDE_TILE]->set_tooltip(TTR("Keep polygon inside region Rect."));
564 toolbar->add_child(tools[SHAPE_KEEP_INSIDE_TILE]);
565 tools[TOOL_GRID_SNAP] = memnew(ToolButton);
566 tools[TOOL_GRID_SNAP]->set_toggle_mode(true);
567 tools[TOOL_GRID_SNAP]->set_tooltip(TTR("Enable snap and show grid (configurable via the Inspector)."));
568 tools[TOOL_GRID_SNAP]->connect("toggled", this, "_on_grid_snap_toggled");
569 toolbar->add_child(tools[TOOL_GRID_SNAP]);
570
571 Control *separator = memnew(Control);
572 separator->set_h_size_flags(SIZE_EXPAND_FILL);
573 toolbar->add_child(separator);
574
575 tools[ZOOM_OUT] = memnew(ToolButton);
576 tools[ZOOM_OUT]->connect("pressed", this, "_zoom_out");
577 toolbar->add_child(tools[ZOOM_OUT]);
578 tools[ZOOM_OUT]->set_tooltip(TTR("Zoom Out"));
579 tools[ZOOM_1] = memnew(ToolButton);
580 tools[ZOOM_1]->connect("pressed", this, "_zoom_reset");
581 toolbar->add_child(tools[ZOOM_1]);
582 tools[ZOOM_1]->set_tooltip(TTR("Zoom Reset"));
583 tools[ZOOM_IN] = memnew(ToolButton);
584 tools[ZOOM_IN]->connect("pressed", this, "_zoom_in");
585 toolbar->add_child(tools[ZOOM_IN]);
586 tools[ZOOM_IN]->set_tooltip(TTR("Zoom In"));
587
588 tools[VISIBLE_INFO] = memnew(ToolButton);
589 tools[VISIBLE_INFO]->set_toggle_mode(true);
590 tools[VISIBLE_INFO]->set_tooltip(TTR("Display Tile Names (Hold Alt Key)"));
591 toolbar->add_child(tools[VISIBLE_INFO]);
592
593 main_vb->add_child(toolbar);
594
595 scroll = memnew(ScrollContainer);
596 main_vb->add_child(scroll);
597 scroll->set_v_size_flags(SIZE_EXPAND_FILL);
598 scroll->connect("gui_input", this, "_on_scroll_container_input");
599 scroll->set_clip_contents(true);
600
601 empty_message = memnew(Label);
602 empty_message->set_text(TTR("Add or select a texture on the left panel to edit the tiles bound to it."));
603 empty_message->set_valign(Label::VALIGN_CENTER);
604 empty_message->set_align(Label::ALIGN_CENTER);
605 empty_message->set_autowrap(true);
606 empty_message->set_custom_minimum_size(Size2(100 * EDSCALE, 0));
607 empty_message->set_v_size_flags(SIZE_EXPAND_FILL);
608 main_vb->add_child(empty_message);
609
610 workspace_container = memnew(Control);
611 scroll->add_child(workspace_container);
612
613 workspace_overlay = memnew(Control);
614 workspace_overlay->connect("draw", this, "_on_workspace_overlay_draw");
615 workspace_container->add_child(workspace_overlay);
616
617 workspace = memnew(Control);
618 workspace->set_focus_mode(FOCUS_ALL);
619 workspace->connect("draw", this, "_on_workspace_draw");
620 workspace->connect("gui_input", this, "_on_workspace_input");
621 workspace->set_draw_behind_parent(true);
622 workspace_overlay->add_child(workspace);
623
624 preview = memnew(Sprite);
625 workspace->add_child(preview);
626 preview->set_centered(false);
627 preview->set_draw_behind_parent(true);
628 preview->set_position(WORKSPACE_MARGIN);
629
630 //---------------
631 cd = memnew(ConfirmationDialog);
632 add_child(cd);
633 cd->connect("confirmed", this, "_on_tileset_toolbar_confirm");
634
635 //---------------
636 err_dialog = memnew(AcceptDialog);
637 add_child(err_dialog);
638
639 //---------------
640 texture_dialog = memnew(EditorFileDialog);
641 texture_dialog->set_access(EditorFileDialog::ACCESS_RESOURCES);
642 texture_dialog->set_mode(EditorFileDialog::MODE_OPEN_FILES);
643 texture_dialog->clear_filters();
644 List<String> extensions;
645
646 ResourceLoader::get_recognized_extensions_for_type("Texture", &extensions);
647 for (List<String>::Element *E = extensions.front(); E; E = E->next()) {
648
649 texture_dialog->add_filter("*." + E->get() + " ; " + E->get().to_upper());
650 }
651 add_child(texture_dialog);
652 texture_dialog->connect("files_selected", this, "_on_textures_added");
653
654 //---------------
655 helper = memnew(TilesetEditorContext(this));
656 tile_names_visible = false;
657
658 // Config scale.
659 max_scale = 16.0f;
660 min_scale = 0.01f;
661 scale_ratio = 1.2f;
662 }
663
~TileSetEditor()664 TileSetEditor::~TileSetEditor() {
665 if (helper)
666 memdelete(helper);
667 }
668
_on_tileset_toolbar_button_pressed(int p_index)669 void TileSetEditor::_on_tileset_toolbar_button_pressed(int p_index) {
670 option = p_index;
671 switch (option) {
672 case TOOL_TILESET_ADD_TEXTURE: {
673 texture_dialog->popup_centered_ratio();
674 } break;
675 case TOOL_TILESET_REMOVE_TEXTURE: {
676 if (get_current_texture().is_valid()) {
677 cd->set_text(TTR("Remove selected texture? This will remove all tiles which use it."));
678 cd->popup_centered(Size2(300, 60));
679 } else {
680 err_dialog->set_text(TTR("You haven't selected a texture to remove."));
681 err_dialog->popup_centered(Size2(300, 60));
682 }
683 } break;
684 case TOOL_TILESET_CREATE_SCENE: {
685
686 cd->set_text(TTR("Create from scene? This will overwrite all current tiles."));
687 cd->popup_centered(Size2(300, 60));
688 } break;
689 case TOOL_TILESET_MERGE_SCENE: {
690
691 cd->set_text(TTR("Merge from scene?"));
692 cd->popup_centered(Size2(300, 60));
693 } break;
694 }
695 }
696
_on_tileset_toolbar_confirm()697 void TileSetEditor::_on_tileset_toolbar_confirm() {
698 switch (option) {
699 case TOOL_TILESET_REMOVE_TEXTURE: {
700 RID current_rid = get_current_texture()->get_rid();
701 List<int> ids;
702 tileset->get_tile_list(&ids);
703
704 undo_redo->create_action(TTR("Remove Texture"));
705 for (List<int>::Element *E = ids.front(); E; E = E->next()) {
706 if (tileset->tile_get_texture(E->get())->get_rid() == current_rid) {
707 undo_redo->add_do_method(tileset.ptr(), "remove_tile", E->get());
708 _undo_tile_removal(E->get());
709 }
710 }
711 undo_redo->add_do_method(this, "remove_texture", get_current_texture());
712 undo_redo->add_undo_method(this, "add_texture", get_current_texture());
713 undo_redo->add_undo_method(this, "update_texture_list_icon");
714 undo_redo->commit_action();
715 } break;
716 case TOOL_TILESET_MERGE_SCENE:
717 case TOOL_TILESET_CREATE_SCENE: {
718
719 EditorNode *en = editor;
720 Node *scene = en->get_edited_scene();
721 if (!scene)
722 break;
723
724 List<int> ids;
725 tileset->get_tile_list(&ids);
726
727 undo_redo->create_action(TTR(option == TOOL_TILESET_MERGE_SCENE ? "Merge Tileset from Scene" : "Create Tileset from Scene"));
728 undo_redo->add_do_method(this, "_undo_redo_import_scene", scene, option == TOOL_TILESET_MERGE_SCENE);
729 undo_redo->add_undo_method(tileset.ptr(), "clear");
730 for (List<int>::Element *E = ids.front(); E; E = E->next()) {
731 _undo_tile_removal(E->get());
732 }
733 undo_redo->add_do_method(this, "edit", tileset);
734 undo_redo->add_undo_method(this, "edit", tileset);
735 undo_redo->commit_action();
736 } break;
737 }
738 }
739
_on_texture_list_selected(int p_index)740 void TileSetEditor::_on_texture_list_selected(int p_index) {
741 if (get_current_texture().is_valid()) {
742 current_item_index = p_index;
743 preview->set_texture(get_current_texture());
744 update_workspace_tile_mode();
745 update_workspace_minsize();
746 } else {
747 current_item_index = -1;
748 preview->set_texture(NULL);
749 workspace->set_custom_minimum_size(Size2i());
750 update_workspace_tile_mode();
751 }
752
753 set_current_tile(-1);
754 workspace->update();
755 }
756
_on_textures_added(const PoolStringArray & p_paths)757 void TileSetEditor::_on_textures_added(const PoolStringArray &p_paths) {
758 int invalid_count = 0;
759 for (int i = 0; i < p_paths.size(); i++) {
760 Ref<Texture> t = Ref<Texture>(ResourceLoader::load(p_paths[i]));
761
762 ERR_CONTINUE_MSG(!t.is_valid(), "'" + p_paths[i] + "' is not a valid texture.");
763
764 if (texture_map.has(t->get_rid())) {
765 invalid_count++;
766 } else {
767 add_texture(t);
768 }
769 }
770
771 if (texture_list->get_item_count() > 0) {
772 update_texture_list_icon();
773 texture_list->select(texture_list->get_item_count() - 1);
774 _on_texture_list_selected(texture_list->get_item_count() - 1);
775 }
776
777 if (invalid_count > 0) {
778 err_dialog->set_text(vformat(TTR("%s file(s) were not added because was already on the list."), String::num(invalid_count, 0)));
779 err_dialog->popup_centered(Size2(300, 60));
780 }
781 }
782
_on_edit_mode_changed(int p_edit_mode)783 void TileSetEditor::_on_edit_mode_changed(int p_edit_mode) {
784 draw_handles = false;
785 creating_shape = false;
786 edit_mode = (EditMode)p_edit_mode;
787 switch (edit_mode) {
788 case EDITMODE_REGION: {
789 tools[TOOL_SELECT]->show();
790
791 separator_bitmask->hide();
792 tools[BITMASK_COPY]->hide();
793 tools[BITMASK_PASTE]->hide();
794 tools[BITMASK_CLEAR]->hide();
795 tools[SHAPE_NEW_POLYGON]->hide();
796 tools[SHAPE_NEW_RECTANGLE]->hide();
797
798 if (workspace_mode == WORKSPACE_EDIT) {
799 separator_delete->show();
800 tools[SHAPE_DELETE]->show();
801 } else {
802 separator_delete->hide();
803 tools[SHAPE_DELETE]->hide();
804 }
805
806 separator_grid->show();
807 tools[SHAPE_KEEP_INSIDE_TILE]->hide();
808 tools[TOOL_GRID_SNAP]->show();
809
810 tools[TOOL_SELECT]->set_pressed(true);
811 tools[TOOL_SELECT]->set_tooltip(TTR("Drag handles to edit Rect.\nClick on another Tile to edit it."));
812 tools[SHAPE_DELETE]->set_tooltip(TTR("Delete selected Rect."));
813 spin_priority->hide();
814 spin_z_index->hide();
815 } break;
816 case EDITMODE_COLLISION:
817 case EDITMODE_OCCLUSION:
818 case EDITMODE_NAVIGATION: {
819 tools[TOOL_SELECT]->show();
820
821 separator_bitmask->hide();
822 tools[BITMASK_COPY]->hide();
823 tools[BITMASK_PASTE]->hide();
824 tools[BITMASK_CLEAR]->hide();
825 tools[SHAPE_NEW_POLYGON]->show();
826 tools[SHAPE_NEW_RECTANGLE]->show();
827
828 separator_delete->show();
829 tools[SHAPE_DELETE]->show();
830
831 separator_grid->show();
832 tools[SHAPE_KEEP_INSIDE_TILE]->show();
833 tools[TOOL_GRID_SNAP]->show();
834
835 tools[TOOL_SELECT]->set_tooltip(TTR("Select current edited sub-tile.\nClick on another Tile to edit it."));
836 tools[SHAPE_DELETE]->set_tooltip(TTR("Delete polygon."));
837 spin_priority->hide();
838 spin_z_index->hide();
839
840 _select_edited_shape_coord();
841 } break;
842 case EDITMODE_BITMASK: {
843 tools[TOOL_SELECT]->show();
844
845 separator_bitmask->show();
846 tools[BITMASK_COPY]->show();
847 tools[BITMASK_PASTE]->show();
848 tools[BITMASK_CLEAR]->show();
849 tools[SHAPE_NEW_POLYGON]->hide();
850 tools[SHAPE_NEW_RECTANGLE]->hide();
851
852 separator_delete->hide();
853 tools[SHAPE_DELETE]->hide();
854
855 tools[SHAPE_KEEP_INSIDE_TILE]->hide();
856
857 tools[TOOL_SELECT]->set_pressed(true);
858 tools[TOOL_SELECT]->set_tooltip(TTR("LMB: Set bit on.\nRMB: Set bit off.\nShift+LMB: Set wildcard bit.\nClick on another Tile to edit it."));
859 spin_priority->hide();
860 } break;
861 case EDITMODE_Z_INDEX:
862 case EDITMODE_PRIORITY:
863 case EDITMODE_ICON: {
864 tools[TOOL_SELECT]->show();
865
866 separator_bitmask->hide();
867 tools[BITMASK_COPY]->hide();
868 tools[BITMASK_PASTE]->hide();
869 tools[BITMASK_CLEAR]->hide();
870 tools[SHAPE_NEW_POLYGON]->hide();
871 tools[SHAPE_NEW_RECTANGLE]->hide();
872
873 separator_delete->hide();
874 tools[SHAPE_DELETE]->hide();
875
876 separator_grid->show();
877 tools[SHAPE_KEEP_INSIDE_TILE]->hide();
878 tools[TOOL_GRID_SNAP]->show();
879
880 if (edit_mode == EDITMODE_ICON) {
881 tools[TOOL_SELECT]->set_tooltip(TTR("Select sub-tile to use as icon, this will be also used on invalid autotile bindings.\nClick on another Tile to edit it."));
882 spin_priority->hide();
883 spin_z_index->hide();
884 } else if (edit_mode == EDITMODE_PRIORITY) {
885 tools[TOOL_SELECT]->set_tooltip(TTR("Select sub-tile to change its priority.\nClick on another Tile to edit it."));
886 spin_priority->show();
887 spin_z_index->hide();
888 } else {
889 tools[TOOL_SELECT]->set_tooltip(TTR("Select sub-tile to change its z index.\nClick on another Tile to edit it."));
890 spin_priority->hide();
891 spin_z_index->show();
892 }
893 } break;
894 default: {
895 }
896 }
897 _update_toggle_shape_button();
898 workspace->update();
899 }
900
_on_workspace_mode_changed(int p_workspace_mode)901 void TileSetEditor::_on_workspace_mode_changed(int p_workspace_mode) {
902 workspace_mode = (WorkspaceMode)p_workspace_mode;
903 if (p_workspace_mode == WORKSPACE_EDIT) {
904 update_workspace_tile_mode();
905 } else {
906 for (int i = 0; i < EDITMODE_MAX; i++) {
907 tool_editmode[i]->hide();
908 }
909 tool_editmode[EDITMODE_REGION]->show();
910 tool_editmode[EDITMODE_REGION]->set_pressed(true);
911 _on_edit_mode_changed(EDITMODE_REGION);
912 separator_editmode->show();
913 }
914 }
915
_on_workspace_draw()916 void TileSetEditor::_on_workspace_draw() {
917
918 if (tileset.is_null() || !get_current_texture().is_valid())
919 return;
920
921 const Color COLOR_AUTOTILE = Color(0.3, 0.6, 1);
922 const Color COLOR_SINGLE = Color(1, 1, 0.3);
923 const Color COLOR_ATLAS = Color(0.8, 0.8, 0.8);
924 const Color COLOR_SUBDIVISION = Color(0.3, 0.7, 0.6);
925
926 draw_handles = false;
927
928 draw_highlight_current_tile();
929
930 draw_grid_snap();
931 if (get_current_tile() >= 0) {
932 int spacing = tileset->autotile_get_spacing(get_current_tile());
933 Vector2 size = tileset->autotile_get_size(get_current_tile());
934 Rect2i region = tileset->tile_get_region(get_current_tile());
935
936 switch (edit_mode) {
937 case EDITMODE_ICON: {
938 Vector2 coord = tileset->autotile_get_icon_coordinate(get_current_tile());
939 draw_highlight_subtile(coord);
940 } break;
941 case EDITMODE_BITMASK: {
942 Color c(1, 0, 0, 0.5);
943 Color ci(0.3, 0.6, 1, 0.5);
944 for (int x = 0; x < region.size.x / (spacing + size.x); x++) {
945 for (int y = 0; y < region.size.y / (spacing + size.y); y++) {
946 Vector2 coord(x, y);
947 Point2 anchor(coord.x * (spacing + size.x), coord.y * (spacing + size.y));
948 anchor += WORKSPACE_MARGIN;
949 anchor += region.position;
950 uint32_t mask = tileset->autotile_get_bitmask(get_current_tile(), coord);
951 if (tileset->autotile_get_bitmask_mode(get_current_tile()) == TileSet::BITMASK_2X2) {
952 if (mask & TileSet::BIND_IGNORE_TOPLEFT) {
953 workspace->draw_rect(Rect2(anchor, size / 4), ci);
954 workspace->draw_rect(Rect2(anchor + size / 4, size / 4), ci);
955 } else if (mask & TileSet::BIND_TOPLEFT) {
956 workspace->draw_rect(Rect2(anchor, size / 2), c);
957 }
958 if (mask & TileSet::BIND_IGNORE_TOPRIGHT) {
959 workspace->draw_rect(Rect2(anchor + Vector2(size.x / 2, 0), size / 4), ci);
960 workspace->draw_rect(Rect2(anchor + Vector2(size.x * 3 / 4, size.y / 4), size / 4), ci);
961 } else if (mask & TileSet::BIND_TOPRIGHT) {
962 workspace->draw_rect(Rect2(anchor + Vector2(size.x / 2, 0), size / 2), c);
963 }
964 if (mask & TileSet::BIND_IGNORE_BOTTOMLEFT) {
965 workspace->draw_rect(Rect2(anchor + Vector2(0, size.y / 2), size / 4), ci);
966 workspace->draw_rect(Rect2(anchor + Vector2(size.x / 4, size.y * 3 / 4), size / 4), ci);
967 } else if (mask & TileSet::BIND_BOTTOMLEFT) {
968 workspace->draw_rect(Rect2(anchor + Vector2(0, size.y / 2), size / 2), c);
969 }
970 if (mask & TileSet::BIND_IGNORE_BOTTOMRIGHT) {
971 workspace->draw_rect(Rect2(anchor + size / 2, size / 4), ci);
972 workspace->draw_rect(Rect2(anchor + size * 3 / 4, size / 4), ci);
973 } else if (mask & TileSet::BIND_BOTTOMRIGHT) {
974 workspace->draw_rect(Rect2(anchor + size / 2, size / 2), c);
975 }
976 } else {
977 if (mask & TileSet::BIND_IGNORE_TOPLEFT) {
978 workspace->draw_rect(Rect2(anchor, size / 6), ci);
979 workspace->draw_rect(Rect2(anchor + size / 6, size / 6), ci);
980 } else if (mask & TileSet::BIND_TOPLEFT) {
981 workspace->draw_rect(Rect2(anchor, size / 3), c);
982 }
983 if (mask & TileSet::BIND_IGNORE_TOP) {
984 workspace->draw_rect(Rect2(anchor + Vector2(size.x / 3, 0), size / 6), ci);
985 workspace->draw_rect(Rect2(anchor + Vector2(size.x / 2, size.y / 6), size / 6), ci);
986 } else if (mask & TileSet::BIND_TOP) {
987 workspace->draw_rect(Rect2(anchor + Vector2(size.x / 3, 0), size / 3), c);
988 }
989 if (mask & TileSet::BIND_IGNORE_TOPRIGHT) {
990 workspace->draw_rect(Rect2(anchor + Vector2(size.x * 4 / 6, 0), size / 6), ci);
991 workspace->draw_rect(Rect2(anchor + Vector2(size.x * 5 / 6, size.y / 6), size / 6), ci);
992 } else if (mask & TileSet::BIND_TOPRIGHT) {
993 workspace->draw_rect(Rect2(anchor + Vector2((size.x / 3) * 2, 0), size / 3), c);
994 }
995 if (mask & TileSet::BIND_IGNORE_LEFT) {
996 workspace->draw_rect(Rect2(anchor + Vector2(0, size.y / 3), size / 6), ci);
997 workspace->draw_rect(Rect2(anchor + Vector2(size.x / 6, size.y / 2), size / 6), ci);
998 } else if (mask & TileSet::BIND_LEFT) {
999 workspace->draw_rect(Rect2(anchor + Vector2(0, size.y / 3), size / 3), c);
1000 }
1001 if (mask & TileSet::BIND_IGNORE_CENTER) {
1002 workspace->draw_rect(Rect2(anchor + size / 3, size / 6), ci);
1003 workspace->draw_rect(Rect2(anchor + size / 2, size / 6), ci);
1004 } else if (mask & TileSet::BIND_CENTER) {
1005 workspace->draw_rect(Rect2(anchor + Vector2(size.x / 3, size.y / 3), size / 3), c);
1006 }
1007 if (mask & TileSet::BIND_IGNORE_RIGHT) {
1008 workspace->draw_rect(Rect2(anchor + Vector2(size.x * 4 / 6, size.y / 3), size / 6), ci);
1009 workspace->draw_rect(Rect2(anchor + Vector2(size.x * 5 / 6, size.y / 2), size / 6), ci);
1010 } else if (mask & TileSet::BIND_RIGHT) {
1011 workspace->draw_rect(Rect2(anchor + Vector2((size.x / 3) * 2, size.y / 3), size / 3), c);
1012 }
1013 if (mask & TileSet::BIND_IGNORE_BOTTOMLEFT) {
1014 workspace->draw_rect(Rect2(anchor + Vector2(0, size.y * 4 / 6), size / 6), ci);
1015 workspace->draw_rect(Rect2(anchor + Vector2(size.x / 6, size.y * 5 / 6), size / 6), ci);
1016 } else if (mask & TileSet::BIND_BOTTOMLEFT) {
1017 workspace->draw_rect(Rect2(anchor + Vector2(0, (size.y / 3) * 2), size / 3), c);
1018 }
1019 if (mask & TileSet::BIND_IGNORE_BOTTOM) {
1020 workspace->draw_rect(Rect2(anchor + Vector2(size.x / 3, size.y * 4 / 6), size / 6), ci);
1021 workspace->draw_rect(Rect2(anchor + Vector2(size.x / 2, size.y * 5 / 6), size / 6), ci);
1022 } else if (mask & TileSet::BIND_BOTTOM) {
1023 workspace->draw_rect(Rect2(anchor + Vector2(size.x / 3, (size.y / 3) * 2), size / 3), c);
1024 }
1025 if (mask & TileSet::BIND_IGNORE_BOTTOMRIGHT) {
1026 workspace->draw_rect(Rect2(anchor + size * 4 / 6, size / 6), ci);
1027 workspace->draw_rect(Rect2(anchor + size * 5 / 6, size / 6), ci);
1028 } else if (mask & TileSet::BIND_BOTTOMRIGHT) {
1029 workspace->draw_rect(Rect2(anchor + (size / 3) * 2, size / 3), c);
1030 }
1031 }
1032 }
1033 }
1034 } break;
1035 case EDITMODE_COLLISION:
1036 case EDITMODE_OCCLUSION:
1037 case EDITMODE_NAVIGATION: {
1038 if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE || tileset->tile_get_tile_mode(get_current_tile()) == TileSet::ATLAS_TILE) {
1039 draw_highlight_subtile(edited_shape_coord);
1040 }
1041 draw_polygon_shapes();
1042 draw_grid_snap();
1043 } break;
1044 case EDITMODE_PRIORITY: {
1045 spin_priority->set_value(tileset->autotile_get_subtile_priority(get_current_tile(), edited_shape_coord));
1046 uint32_t mask = tileset->autotile_get_bitmask(get_current_tile(), edited_shape_coord);
1047 Vector<Vector2> queue_others;
1048 int total = 0;
1049 for (Map<Vector2, uint32_t>::Element *E = tileset->autotile_get_bitmask_map(get_current_tile()).front(); E; E = E->next()) {
1050 if (E->value() == mask) {
1051 total += tileset->autotile_get_subtile_priority(get_current_tile(), E->key());
1052 if (E->key() != edited_shape_coord) {
1053 queue_others.push_back(E->key());
1054 }
1055 }
1056 }
1057 spin_priority->set_suffix(" / " + String::num(total, 0));
1058 draw_highlight_subtile(edited_shape_coord, queue_others);
1059 } break;
1060 case EDITMODE_Z_INDEX: {
1061 spin_z_index->set_value(tileset->autotile_get_z_index(get_current_tile(), edited_shape_coord));
1062 draw_highlight_subtile(edited_shape_coord);
1063 } break;
1064 default: {
1065 }
1066 }
1067 }
1068
1069 RID current_texture_rid = get_current_texture()->get_rid();
1070 List<int> *tiles = new List<int>();
1071 tileset->get_tile_list(tiles);
1072 for (List<int>::Element *E = tiles->front(); E; E = E->next()) {
1073 int t_id = E->get();
1074 if (tileset->tile_get_texture(t_id)->get_rid() == current_texture_rid && (t_id != get_current_tile() || edit_mode != EDITMODE_REGION || workspace_mode != WORKSPACE_EDIT)) {
1075 Rect2i region = tileset->tile_get_region(t_id);
1076 region.position += WORKSPACE_MARGIN;
1077 Color c;
1078 if (tileset->tile_get_tile_mode(t_id) == TileSet::SINGLE_TILE)
1079 c = COLOR_SINGLE;
1080 else if (tileset->tile_get_tile_mode(t_id) == TileSet::AUTO_TILE)
1081 c = COLOR_AUTOTILE;
1082 else if (tileset->tile_get_tile_mode(t_id) == TileSet::ATLAS_TILE)
1083 c = COLOR_ATLAS;
1084 draw_tile_subdivision(t_id, COLOR_SUBDIVISION);
1085 workspace->draw_rect(region, c, false);
1086 }
1087 }
1088 delete tiles;
1089
1090 if (edit_mode == EDITMODE_REGION) {
1091 if (workspace_mode != WORKSPACE_EDIT) {
1092 Rect2i region = edited_region;
1093 Color c;
1094 if (workspace_mode == WORKSPACE_CREATE_SINGLE)
1095 c = COLOR_SINGLE;
1096 else if (workspace_mode == WORKSPACE_CREATE_AUTOTILE)
1097 c = COLOR_AUTOTILE;
1098 else if (workspace_mode == WORKSPACE_CREATE_ATLAS)
1099 c = COLOR_ATLAS;
1100 workspace->draw_rect(region, c, false);
1101 draw_edited_region_subdivision();
1102 } else {
1103 int t_id = get_current_tile();
1104 if (t_id < 0)
1105 return;
1106
1107 Rect2i region;
1108 if (draw_edited_region)
1109 region = edited_region;
1110 else {
1111 region = tileset->tile_get_region(t_id);
1112 region.position += WORKSPACE_MARGIN;
1113 }
1114
1115 if (draw_edited_region)
1116 draw_edited_region_subdivision();
1117 else
1118 draw_tile_subdivision(t_id, COLOR_SUBDIVISION);
1119
1120 Color c;
1121 if (tileset->tile_get_tile_mode(t_id) == TileSet::SINGLE_TILE)
1122 c = COLOR_SINGLE;
1123 else if (tileset->tile_get_tile_mode(t_id) == TileSet::AUTO_TILE)
1124 c = COLOR_AUTOTILE;
1125 else if (tileset->tile_get_tile_mode(t_id) == TileSet::ATLAS_TILE)
1126 c = COLOR_ATLAS;
1127 workspace->draw_rect(region, c, false);
1128 }
1129 }
1130
1131 workspace_overlay->update();
1132 }
1133
_on_workspace_process()1134 void TileSetEditor::_on_workspace_process() {
1135
1136 if (Input::get_singleton()->is_key_pressed(KEY_ALT) || tools[VISIBLE_INFO]->is_pressed()) {
1137 if (!tile_names_visible) {
1138 tile_names_visible = true;
1139 workspace_overlay->update();
1140 }
1141 } else if (tile_names_visible) {
1142 tile_names_visible = false;
1143 workspace_overlay->update();
1144 }
1145 }
1146
_on_workspace_overlay_draw()1147 void TileSetEditor::_on_workspace_overlay_draw() {
1148
1149 if (!tileset.is_valid() || !get_current_texture().is_valid())
1150 return;
1151
1152 const Color COLOR_AUTOTILE = Color(0.266373, 0.565288, 0.988281);
1153 const Color COLOR_SINGLE = Color(0.988281, 0.909323, 0.266373);
1154 const Color COLOR_ATLAS = Color(0.78653, 0.812835, 0.832031);
1155
1156 if (tile_names_visible) {
1157 RID current_texture_rid = get_current_texture()->get_rid();
1158 List<int> *tiles = new List<int>();
1159 tileset->get_tile_list(tiles);
1160 for (List<int>::Element *E = tiles->front(); E; E = E->next()) {
1161 int t_id = E->get();
1162 if (tileset->tile_get_texture(t_id)->get_rid() != current_texture_rid)
1163 continue;
1164
1165 Rect2 region = tileset->tile_get_region(t_id);
1166 region.position += WORKSPACE_MARGIN;
1167 region.position *= workspace->get_scale().x;
1168 Color c;
1169 if (tileset->tile_get_tile_mode(t_id) == TileSet::SINGLE_TILE)
1170 c = COLOR_SINGLE;
1171 else if (tileset->tile_get_tile_mode(t_id) == TileSet::AUTO_TILE)
1172 c = COLOR_AUTOTILE;
1173 else if (tileset->tile_get_tile_mode(t_id) == TileSet::ATLAS_TILE)
1174 c = COLOR_ATLAS;
1175 String tile_id_name = String::num(t_id, 0) + ": " + tileset->tile_get_name(t_id);
1176 Ref<Font> font = get_font("font", "Label");
1177 region.set_size(font->get_string_size(tile_id_name));
1178 workspace_overlay->draw_rect(region, c);
1179 region.position.y += region.size.y - 2;
1180 c = Color(0.1, 0.1, 0.1);
1181 workspace_overlay->draw_string(font, region.position, tile_id_name, c);
1182 }
1183 delete tiles;
1184 }
1185
1186 int t_id = get_current_tile();
1187 if (t_id < 0)
1188 return;
1189
1190 Ref<Texture> handle = get_icon("EditorHandle", "EditorIcons");
1191 if (draw_handles) {
1192 for (int i = 0; i < current_shape.size(); i++) {
1193 workspace_overlay->draw_texture(handle, current_shape[i] * workspace->get_scale().x - handle->get_size() * 0.5);
1194 }
1195 }
1196 }
1197
get_grabbed_point(const Vector2 & p_mouse_pos,real_t p_grab_threshold)1198 int TileSetEditor::get_grabbed_point(const Vector2 &p_mouse_pos, real_t p_grab_threshold) {
1199 Transform2D xform = workspace->get_transform();
1200
1201 int grabbed_point = -1;
1202 real_t min_distance = 1e10;
1203
1204 for (int i = 0; i < current_shape.size(); i++) {
1205 const real_t distance = xform.xform(current_shape[i]).distance_to(xform.xform(p_mouse_pos));
1206 if (distance < p_grab_threshold && distance < min_distance) {
1207 min_distance = distance;
1208 grabbed_point = i;
1209 }
1210 }
1211
1212 return grabbed_point;
1213 }
1214
is_within_grabbing_distance_of_first_point(const Vector2 & p_pos,real_t p_grab_threshold)1215 bool TileSetEditor::is_within_grabbing_distance_of_first_point(const Vector2 &p_pos, real_t p_grab_threshold) {
1216 Transform2D xform = workspace->get_transform();
1217
1218 const real_t distance = xform.xform(current_shape[0]).distance_to(xform.xform(p_pos));
1219
1220 return distance < p_grab_threshold;
1221 }
1222
_on_scroll_container_input(const Ref<InputEvent> & p_event)1223 void TileSetEditor::_on_scroll_container_input(const Ref<InputEvent> &p_event) {
1224 const Ref<InputEventMouseButton> mb = p_event;
1225
1226 if (mb.is_valid()) {
1227 // Zoom in/out using Ctrl + mouse wheel. This is done on the ScrollContainer
1228 // to allow performing this action anywhere, even if the cursor isn't
1229 // hovering the texture in the workspace.
1230 if (mb->get_button_index() == BUTTON_WHEEL_UP && mb->is_pressed() && mb->get_control()) {
1231 print_line("zooming in");
1232 _zoom_in();
1233 // Don't scroll up after zooming in.
1234 accept_event();
1235 } else if (mb->get_button_index() == BUTTON_WHEEL_DOWN && mb->is_pressed() && mb->get_control()) {
1236 print_line("zooming out");
1237 _zoom_out();
1238 // Don't scroll down after zooming out.
1239 accept_event();
1240 }
1241 }
1242 }
1243
_on_workspace_input(const Ref<InputEvent> & p_ie)1244 void TileSetEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) {
1245
1246 if (tileset.is_null() || !get_current_texture().is_valid())
1247 return;
1248
1249 static bool dragging;
1250 static bool erasing;
1251 static bool alternative;
1252 draw_edited_region = false;
1253
1254 Rect2 current_tile_region = Rect2();
1255 if (get_current_tile() >= 0) {
1256 current_tile_region = tileset->tile_get_region(get_current_tile());
1257 }
1258 current_tile_region.position += WORKSPACE_MARGIN;
1259
1260 const Ref<InputEventMouseButton> mb = p_ie;
1261 const Ref<InputEventMouseMotion> mm = p_ie;
1262
1263 if (mb.is_valid()) {
1264 if (mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT && !creating_shape) {
1265 if (!current_tile_region.has_point(mb->get_position())) {
1266 List<int> *tiles = new List<int>();
1267 tileset->get_tile_list(tiles);
1268 for (List<int>::Element *E = tiles->front(); E; E = E->next()) {
1269 int t_id = E->get();
1270 if (get_current_texture()->get_rid() == tileset->tile_get_texture(t_id)->get_rid()) {
1271 Rect2 r = tileset->tile_get_region(t_id);
1272 r.position += WORKSPACE_MARGIN;
1273 if (r.has_point(mb->get_position())) {
1274 set_current_tile(t_id);
1275 workspace->update();
1276 workspace_overlay->update();
1277 delete tiles;
1278 return;
1279 }
1280 }
1281 }
1282 delete tiles;
1283 }
1284 }
1285 }
1286 // Drag Middle Mouse
1287 if (mm.is_valid()) {
1288 if (mm->get_button_mask() & BUTTON_MASK_MIDDLE) {
1289 Vector2 dragged(mm->get_relative().x, mm->get_relative().y);
1290 scroll->set_h_scroll(scroll->get_h_scroll() - dragged.x * workspace->get_scale().x);
1291 scroll->set_v_scroll(scroll->get_v_scroll() - dragged.y * workspace->get_scale().x);
1292 }
1293 }
1294
1295 if (edit_mode == EDITMODE_REGION) {
1296 if (mb.is_valid()) {
1297 if (mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) {
1298 if (get_current_tile() >= 0 || workspace_mode != WORKSPACE_EDIT) {
1299 dragging = true;
1300 region_from = mb->get_position();
1301 edited_region = Rect2(region_from, Size2());
1302 workspace->update();
1303 workspace_overlay->update();
1304 return;
1305 }
1306 } else if (dragging && mb->is_pressed() && mb->get_button_index() == BUTTON_RIGHT) {
1307 dragging = false;
1308 edited_region = Rect2();
1309 workspace->update();
1310 workspace_overlay->update();
1311 return;
1312 } else if (dragging && !mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) {
1313 dragging = false;
1314 update_edited_region(mb->get_position());
1315 edited_region.position -= WORKSPACE_MARGIN;
1316 if (!edited_region.has_no_area()) {
1317 if (get_current_tile() >= 0 && workspace_mode == WORKSPACE_EDIT) {
1318 undo_redo->create_action(TTR("Set Tile Region"));
1319 undo_redo->add_do_method(tileset.ptr(), "tile_set_region", get_current_tile(), edited_region);
1320 undo_redo->add_undo_method(tileset.ptr(), "tile_set_region", get_current_tile(), tileset->tile_get_region(get_current_tile()));
1321
1322 Size2 tile_workspace_size = edited_region.position + edited_region.size + WORKSPACE_MARGIN * 2;
1323 Size2 workspace_minsize = workspace->get_custom_minimum_size();
1324 // If the new region is bigger, just directly change the workspace size to avoid checking all other tiles.
1325 if (tile_workspace_size.x > workspace_minsize.x || tile_workspace_size.y > workspace_minsize.y) {
1326 Size2 max_workspace_size = Size2(MAX(tile_workspace_size.x, workspace_minsize.x), MAX(tile_workspace_size.y, workspace_minsize.y));
1327 undo_redo->add_do_method(workspace, "set_custom_minimum_size", max_workspace_size);
1328 undo_redo->add_undo_method(workspace, "set_custom_minimum_size", workspace_minsize);
1329 undo_redo->add_do_method(workspace_container, "set_custom_minimum_size", max_workspace_size);
1330 undo_redo->add_undo_method(workspace_container, "set_custom_minimum_size", workspace_minsize);
1331 undo_redo->add_do_method(workspace_overlay, "set_custom_minimum_size", max_workspace_size);
1332 undo_redo->add_undo_method(workspace_overlay, "set_custom_minimum_size", workspace_minsize);
1333 } else if (workspace_minsize.x > get_current_texture()->get_size().x + WORKSPACE_MARGIN.x * 2 || workspace_minsize.y > get_current_texture()->get_size().y + WORKSPACE_MARGIN.y * 2) {
1334 undo_redo->add_do_method(this, "update_workspace_minsize");
1335 undo_redo->add_undo_method(this, "update_workspace_minsize");
1336 }
1337
1338 edited_region = Rect2();
1339
1340 undo_redo->add_do_method(workspace, "update");
1341 undo_redo->add_undo_method(workspace, "update");
1342 undo_redo->add_do_method(workspace_overlay, "update");
1343 undo_redo->add_undo_method(workspace_overlay, "update");
1344 undo_redo->commit_action();
1345 } else {
1346 int t_id = tileset->get_last_unused_tile_id();
1347 undo_redo->create_action(TTR("Create Tile"));
1348 undo_redo->add_do_method(tileset.ptr(), "create_tile", t_id);
1349 undo_redo->add_undo_method(tileset.ptr(), "remove_tile", t_id);
1350 undo_redo->add_undo_method(this, "_validate_current_tile_id");
1351 undo_redo->add_do_method(tileset.ptr(), "tile_set_texture", t_id, get_current_texture());
1352 undo_redo->add_do_method(tileset.ptr(), "tile_set_region", t_id, edited_region);
1353 undo_redo->add_do_method(tileset.ptr(), "tile_set_name", t_id, get_current_texture()->get_path().get_file() + " " + String::num(t_id, 0));
1354 if (workspace_mode != WORKSPACE_CREATE_SINGLE) {
1355 undo_redo->add_do_method(tileset.ptr(), "autotile_set_size", t_id, snap_step);
1356 undo_redo->add_do_method(tileset.ptr(), "autotile_set_spacing", t_id, snap_separation.x);
1357 undo_redo->add_do_method(tileset.ptr(), "tile_set_tile_mode", t_id, workspace_mode == WORKSPACE_CREATE_AUTOTILE ? TileSet::AUTO_TILE : TileSet::ATLAS_TILE);
1358 }
1359
1360 tool_workspacemode[WORKSPACE_EDIT]->set_pressed(true);
1361 tool_editmode[EDITMODE_COLLISION]->set_pressed(true);
1362 edit_mode = EDITMODE_COLLISION;
1363
1364 Size2 tile_workspace_size = edited_region.position + edited_region.size + WORKSPACE_MARGIN * 2;
1365 Size2 workspace_minsize = workspace->get_custom_minimum_size();
1366 if (tile_workspace_size.x > workspace_minsize.x || tile_workspace_size.y > workspace_minsize.y) {
1367 Size2 new_workspace_minsize = Size2(MAX(tile_workspace_size.x, workspace_minsize.x), MAX(tile_workspace_size.y, workspace_minsize.y));
1368 undo_redo->add_do_method(workspace, "set_custom_minimum_size", new_workspace_minsize);
1369 undo_redo->add_undo_method(workspace, "set_custom_minimum_size", workspace_minsize);
1370 undo_redo->add_do_method(workspace_container, "set_custom_minimum_size", new_workspace_minsize);
1371 undo_redo->add_undo_method(workspace_container, "set_custom_minimum_size", workspace_minsize);
1372 undo_redo->add_do_method(workspace_overlay, "set_custom_minimum_size", new_workspace_minsize);
1373 undo_redo->add_undo_method(workspace_overlay, "set_custom_minimum_size", workspace_minsize);
1374 }
1375
1376 edited_region = Rect2();
1377
1378 undo_redo->add_do_method(workspace, "update");
1379 undo_redo->add_undo_method(workspace, "update");
1380 undo_redo->add_do_method(workspace_overlay, "update");
1381 undo_redo->add_undo_method(workspace_overlay, "update");
1382 undo_redo->commit_action();
1383
1384 set_current_tile(t_id);
1385 _on_workspace_mode_changed(WORKSPACE_EDIT);
1386 }
1387 } else {
1388 edited_region = Rect2();
1389 workspace->update();
1390 workspace_overlay->update();
1391 }
1392 return;
1393 }
1394 } else if (mm.is_valid()) {
1395 if (dragging) {
1396 update_edited_region(mm->get_position());
1397 draw_edited_region = true;
1398 workspace->update();
1399 workspace_overlay->update();
1400 return;
1401 }
1402 }
1403 }
1404
1405 if (workspace_mode == WORKSPACE_EDIT) {
1406 if (get_current_tile() >= 0) {
1407 int spacing = tileset->autotile_get_spacing(get_current_tile());
1408 Vector2 size = tileset->autotile_get_size(get_current_tile());
1409 switch (edit_mode) {
1410 case EDITMODE_ICON: {
1411 if (mb.is_valid()) {
1412 if (mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT && current_tile_region.has_point(mb->get_position())) {
1413 Vector2 coord((int)((mb->get_position().x - current_tile_region.position.x) / (spacing + size.x)), (int)((mb->get_position().y - current_tile_region.position.y) / (spacing + size.y)));
1414 undo_redo->create_action(TTR("Set Tile Icon"));
1415 undo_redo->add_do_method(tileset.ptr(), "autotile_set_icon_coordinate", get_current_tile(), coord);
1416 undo_redo->add_undo_method(tileset.ptr(), "autotile_set_icon_coordinate", get_current_tile(), tileset->autotile_get_icon_coordinate(get_current_tile()));
1417 undo_redo->add_do_method(workspace, "update");
1418 undo_redo->add_undo_method(workspace, "update");
1419 undo_redo->commit_action();
1420 }
1421 }
1422 } break;
1423 case EDITMODE_BITMASK: {
1424 if (mb.is_valid()) {
1425 if (mb->is_pressed()) {
1426 if (dragging) {
1427 return;
1428 }
1429 if ((mb->get_button_index() == BUTTON_RIGHT || mb->get_button_index() == BUTTON_LEFT) && current_tile_region.has_point(mb->get_position())) {
1430 dragging = true;
1431 erasing = (mb->get_button_index() == BUTTON_RIGHT);
1432 alternative = Input::get_singleton()->is_key_pressed(KEY_SHIFT);
1433 Vector2 coord((int)((mb->get_position().x - current_tile_region.position.x) / (spacing + size.x)), (int)((mb->get_position().y - current_tile_region.position.y) / (spacing + size.y)));
1434 Vector2 pos(coord.x * (spacing + size.x), coord.y * (spacing + size.y));
1435 pos = mb->get_position() - (pos + current_tile_region.position);
1436 uint32_t bit = 0;
1437 if (tileset->autotile_get_bitmask_mode(get_current_tile()) == TileSet::BITMASK_2X2) {
1438 if (pos.x < size.x / 2) {
1439 if (pos.y < size.y / 2) {
1440 bit = TileSet::BIND_TOPLEFT;
1441 } else {
1442 bit = TileSet::BIND_BOTTOMLEFT;
1443 }
1444 } else {
1445 if (pos.y < size.y / 2) {
1446 bit = TileSet::BIND_TOPRIGHT;
1447 } else {
1448 bit = TileSet::BIND_BOTTOMRIGHT;
1449 }
1450 }
1451 } else {
1452 if (pos.x < size.x / 3) {
1453 if (pos.y < size.y / 3) {
1454 bit = TileSet::BIND_TOPLEFT;
1455 } else if (pos.y > (size.y / 3) * 2) {
1456 bit = TileSet::BIND_BOTTOMLEFT;
1457 } else {
1458 bit = TileSet::BIND_LEFT;
1459 }
1460 } else if (pos.x > (size.x / 3) * 2) {
1461 if (pos.y < size.y / 3) {
1462 bit = TileSet::BIND_TOPRIGHT;
1463 } else if (pos.y > (size.y / 3) * 2) {
1464 bit = TileSet::BIND_BOTTOMRIGHT;
1465 } else {
1466 bit = TileSet::BIND_RIGHT;
1467 }
1468 } else {
1469 if (pos.y < size.y / 3) {
1470 bit = TileSet::BIND_TOP;
1471 } else if (pos.y > (size.y / 3) * 2) {
1472 bit = TileSet::BIND_BOTTOM;
1473 } else {
1474 bit = TileSet::BIND_CENTER;
1475 }
1476 }
1477 }
1478
1479 uint32_t old_mask = tileset->autotile_get_bitmask(get_current_tile(), coord);
1480 uint32_t new_mask = old_mask;
1481 if (alternative) {
1482 new_mask &= ~bit;
1483 new_mask |= (bit << 16);
1484 } else if (erasing) {
1485 new_mask &= ~bit;
1486 new_mask &= ~(bit << 16);
1487 } else {
1488 new_mask |= bit;
1489 new_mask &= ~(bit << 16);
1490 }
1491
1492 if (old_mask != new_mask) {
1493 undo_redo->create_action(TTR("Edit Tile Bitmask"));
1494 undo_redo->add_do_method(tileset.ptr(), "autotile_set_bitmask", get_current_tile(), coord, new_mask);
1495 undo_redo->add_undo_method(tileset.ptr(), "autotile_set_bitmask", get_current_tile(), coord, old_mask);
1496 undo_redo->add_do_method(workspace, "update");
1497 undo_redo->add_undo_method(workspace, "update");
1498 undo_redo->commit_action();
1499 }
1500 }
1501 } else {
1502 if ((erasing && mb->get_button_index() == BUTTON_RIGHT) || (!erasing && mb->get_button_index() == BUTTON_LEFT)) {
1503 dragging = false;
1504 erasing = false;
1505 alternative = false;
1506 }
1507 }
1508 }
1509 if (mm.is_valid()) {
1510 if (dragging && current_tile_region.has_point(mm->get_position())) {
1511 Vector2 coord((int)((mm->get_position().x - current_tile_region.position.x) / (spacing + size.x)), (int)((mm->get_position().y - current_tile_region.position.y) / (spacing + size.y)));
1512 Vector2 pos(coord.x * (spacing + size.x), coord.y * (spacing + size.y));
1513 pos = mm->get_position() - (pos + current_tile_region.position);
1514 uint32_t bit = 0;
1515 if (tileset->autotile_get_bitmask_mode(get_current_tile()) == TileSet::BITMASK_2X2) {
1516 if (pos.x < size.x / 2) {
1517 if (pos.y < size.y / 2) {
1518 bit = TileSet::BIND_TOPLEFT;
1519 } else {
1520 bit = TileSet::BIND_BOTTOMLEFT;
1521 }
1522 } else {
1523 if (pos.y < size.y / 2) {
1524 bit = TileSet::BIND_TOPRIGHT;
1525 } else {
1526 bit = TileSet::BIND_BOTTOMRIGHT;
1527 }
1528 }
1529 } else {
1530 if (pos.x < size.x / 3) {
1531 if (pos.y < size.y / 3) {
1532 bit = TileSet::BIND_TOPLEFT;
1533 } else if (pos.y > (size.y / 3) * 2) {
1534 bit = TileSet::BIND_BOTTOMLEFT;
1535 } else {
1536 bit = TileSet::BIND_LEFT;
1537 }
1538 } else if (pos.x > (size.x / 3) * 2) {
1539 if (pos.y < size.y / 3) {
1540 bit = TileSet::BIND_TOPRIGHT;
1541 } else if (pos.y > (size.y / 3) * 2) {
1542 bit = TileSet::BIND_BOTTOMRIGHT;
1543 } else {
1544 bit = TileSet::BIND_RIGHT;
1545 }
1546 } else {
1547 if (pos.y < size.y / 3) {
1548 bit = TileSet::BIND_TOP;
1549 } else if (pos.y > (size.y / 3) * 2) {
1550 bit = TileSet::BIND_BOTTOM;
1551 } else {
1552 bit = TileSet::BIND_CENTER;
1553 }
1554 }
1555 }
1556
1557 uint32_t old_mask = tileset->autotile_get_bitmask(get_current_tile(), coord);
1558 uint32_t new_mask = old_mask;
1559 if (alternative) {
1560 new_mask &= ~bit;
1561 new_mask |= (bit << 16);
1562 } else if (erasing) {
1563 new_mask &= ~bit;
1564 new_mask &= ~(bit << 16);
1565 } else {
1566 new_mask |= bit;
1567 new_mask &= ~(bit << 16);
1568 }
1569 if (old_mask != new_mask) {
1570 undo_redo->create_action(TTR("Edit Tile Bitmask"));
1571 undo_redo->add_do_method(tileset.ptr(), "autotile_set_bitmask", get_current_tile(), coord, new_mask);
1572 undo_redo->add_undo_method(tileset.ptr(), "autotile_set_bitmask", get_current_tile(), coord, old_mask);
1573 undo_redo->add_do_method(workspace, "update");
1574 undo_redo->add_undo_method(workspace, "update");
1575 undo_redo->commit_action();
1576 }
1577 }
1578 }
1579 } break;
1580 case EDITMODE_COLLISION:
1581 case EDITMODE_OCCLUSION:
1582 case EDITMODE_NAVIGATION:
1583 case EDITMODE_PRIORITY:
1584 case EDITMODE_Z_INDEX: {
1585 Vector2 shape_anchor = Vector2(0, 0);
1586 if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE || tileset->tile_get_tile_mode(get_current_tile()) == TileSet::ATLAS_TILE) {
1587 shape_anchor = edited_shape_coord;
1588 shape_anchor.x *= (size.x + spacing);
1589 shape_anchor.y *= (size.y + spacing);
1590 }
1591
1592 const real_t grab_threshold = EDITOR_GET("editors/poly_editor/point_grab_radius");
1593 shape_anchor += current_tile_region.position;
1594 if (tools[TOOL_SELECT]->is_pressed()) {
1595 if (mb.is_valid()) {
1596 if (mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) {
1597 if (edit_mode != EDITMODE_PRIORITY && current_shape.size() > 0) {
1598 int grabbed_point = get_grabbed_point(mb->get_position(), grab_threshold);
1599
1600 if (grabbed_point >= 0) {
1601 dragging_point = grabbed_point;
1602 workspace->update();
1603 return;
1604 }
1605 }
1606 if ((tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE || tileset->tile_get_tile_mode(get_current_tile()) == TileSet::ATLAS_TILE) && current_tile_region.has_point(mb->get_position())) {
1607 Vector2 coord((int)((mb->get_position().x - current_tile_region.position.x) / (spacing + size.x)), (int)((mb->get_position().y - current_tile_region.position.y) / (spacing + size.y)));
1608 if (edited_shape_coord != coord) {
1609 edited_shape_coord = coord;
1610 _select_edited_shape_coord();
1611 }
1612 }
1613 workspace->update();
1614 } else if (!mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) {
1615 if (edit_mode == EDITMODE_COLLISION) {
1616 if (dragging_point >= 0) {
1617 dragging_point = -1;
1618
1619 Vector<Vector2> points;
1620
1621 for (int i = 0; i < current_shape.size(); i++) {
1622 Vector2 p = current_shape[i];
1623 if (tools[TOOL_GRID_SNAP]->is_pressed() || tools[SHAPE_KEEP_INSIDE_TILE]->is_pressed()) {
1624 p = snap_point(p);
1625 }
1626 points.push_back(p - shape_anchor);
1627 }
1628
1629 undo_redo->create_action(TTR("Edit Collision Polygon"));
1630 _set_edited_shape_points(points);
1631 undo_redo->add_do_method(this, "_select_edited_shape_coord");
1632 undo_redo->add_undo_method(this, "_select_edited_shape_coord");
1633 undo_redo->commit_action();
1634 }
1635 } else if (edit_mode == EDITMODE_OCCLUSION) {
1636 if (dragging_point >= 0) {
1637 dragging_point = -1;
1638
1639 PoolVector<Vector2> polygon;
1640 polygon.resize(current_shape.size());
1641 PoolVector<Vector2>::Write w = polygon.write();
1642
1643 for (int i = 0; i < current_shape.size(); i++) {
1644 w[i] = current_shape[i] - shape_anchor;
1645 }
1646
1647 w.release();
1648
1649 undo_redo->create_action(TTR("Edit Occlusion Polygon"));
1650 undo_redo->add_do_method(edited_occlusion_shape.ptr(), "set_polygon", polygon);
1651 undo_redo->add_undo_method(edited_occlusion_shape.ptr(), "set_polygon", edited_occlusion_shape->get_polygon());
1652 undo_redo->add_do_method(this, "_select_edited_shape_coord");
1653 undo_redo->add_undo_method(this, "_select_edited_shape_coord");
1654 undo_redo->commit_action();
1655 }
1656 } else if (edit_mode == EDITMODE_NAVIGATION) {
1657 if (dragging_point >= 0) {
1658 dragging_point = -1;
1659
1660 PoolVector<Vector2> polygon;
1661 Vector<int> indices;
1662 polygon.resize(current_shape.size());
1663 PoolVector<Vector2>::Write w = polygon.write();
1664
1665 for (int i = 0; i < current_shape.size(); i++) {
1666 w[i] = current_shape[i] - shape_anchor;
1667 indices.push_back(i);
1668 }
1669
1670 w.release();
1671
1672 undo_redo->create_action(TTR("Edit Navigation Polygon"));
1673 undo_redo->add_do_method(edited_navigation_shape.ptr(), "set_vertices", polygon);
1674 undo_redo->add_undo_method(edited_navigation_shape.ptr(), "set_vertices", edited_navigation_shape->get_vertices());
1675 undo_redo->add_do_method(edited_navigation_shape.ptr(), "clear_polygons");
1676 undo_redo->add_undo_method(edited_navigation_shape.ptr(), "clear_polygons");
1677 undo_redo->add_do_method(edited_navigation_shape.ptr(), "add_polygon", indices);
1678 undo_redo->add_undo_method(edited_navigation_shape.ptr(), "add_polygon", edited_navigation_shape->get_polygon(0));
1679 undo_redo->add_do_method(this, "_select_edited_shape_coord");
1680 undo_redo->add_undo_method(this, "_select_edited_shape_coord");
1681 undo_redo->commit_action();
1682 }
1683 }
1684 }
1685 } else if (mm.is_valid()) {
1686 if (dragging_point >= 0) {
1687 current_shape.set(dragging_point, snap_point(mm->get_position()));
1688 workspace->update();
1689 }
1690 }
1691 } else if (tools[SHAPE_NEW_POLYGON]->is_pressed()) {
1692 if (mb.is_valid()) {
1693 if (mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) {
1694 Vector2 pos = mb->get_position();
1695 pos = snap_point(pos);
1696 if (creating_shape) {
1697 if (current_shape.size() > 2) {
1698
1699 if (is_within_grabbing_distance_of_first_point(mb->get_position(), grab_threshold)) {
1700 close_shape(shape_anchor);
1701 workspace->update();
1702 return;
1703 }
1704 }
1705 current_shape.push_back(pos);
1706 workspace->update();
1707 } else {
1708 creating_shape = true;
1709 _set_edited_collision_shape(Ref<ConvexPolygonShape2D>());
1710 current_shape.resize(0);
1711 current_shape.push_back(snap_point(pos));
1712 workspace->update();
1713 }
1714 } else if (mb->is_pressed() && mb->get_button_index() == BUTTON_RIGHT) {
1715 if (creating_shape) {
1716 creating_shape = false;
1717 _select_edited_shape_coord();
1718 workspace->update();
1719 }
1720 }
1721 } else if (mm.is_valid()) {
1722 if (creating_shape) {
1723 workspace->update();
1724 }
1725 }
1726 } else if (tools[SHAPE_NEW_RECTANGLE]->is_pressed()) {
1727 if (mb.is_valid()) {
1728 if (mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) {
1729 _set_edited_collision_shape(Ref<ConvexPolygonShape2D>());
1730 current_shape.resize(0);
1731 Vector2 pos = mb->get_position();
1732 pos = snap_point(pos);
1733 current_shape.push_back(pos);
1734 current_shape.push_back(pos);
1735 current_shape.push_back(pos);
1736 current_shape.push_back(pos);
1737 creating_shape = true;
1738 workspace->update();
1739 return;
1740 } else if (mb->is_pressed() && mb->get_button_index() == BUTTON_RIGHT) {
1741 if (creating_shape) {
1742 creating_shape = false;
1743 _select_edited_shape_coord();
1744 workspace->update();
1745 }
1746 } else if (!mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) {
1747 if (creating_shape) {
1748
1749 // if the first two corners are within grabbing distance of one another, expand the rect to fill the tile
1750 if (is_within_grabbing_distance_of_first_point(current_shape[1], grab_threshold)) {
1751 current_shape.set(0, snap_point(shape_anchor));
1752 current_shape.set(1, snap_point(shape_anchor + Vector2(current_tile_region.size.x, 0)));
1753 current_shape.set(2, snap_point(shape_anchor + current_tile_region.size));
1754 current_shape.set(3, snap_point(shape_anchor + Vector2(0, current_tile_region.size.y)));
1755 }
1756
1757 close_shape(shape_anchor);
1758 workspace->update();
1759 return;
1760 }
1761 }
1762 } else if (mm.is_valid()) {
1763 if (creating_shape) {
1764 Vector2 pos = mm->get_position();
1765 pos = snap_point(pos);
1766 Vector2 p = current_shape[2];
1767 current_shape.set(3, snap_point(Vector2(pos.x, p.y)));
1768 current_shape.set(0, snap_point(pos));
1769 current_shape.set(1, snap_point(Vector2(p.x, pos.y)));
1770 workspace->update();
1771 }
1772 }
1773 }
1774 } break;
1775 default: {
1776 }
1777 }
1778 }
1779 }
1780 }
1781
_on_tool_clicked(int p_tool)1782 void TileSetEditor::_on_tool_clicked(int p_tool) {
1783 if (p_tool == BITMASK_COPY) {
1784 bitmask_map_copy = tileset->autotile_get_bitmask_map(get_current_tile());
1785 } else if (p_tool == BITMASK_PASTE) {
1786 undo_redo->create_action(TTR("Paste Tile Bitmask"));
1787 undo_redo->add_do_method(tileset.ptr(), "autotile_clear_bitmask_map", get_current_tile());
1788 undo_redo->add_undo_method(tileset.ptr(), "autotile_clear_bitmask_map", get_current_tile());
1789 for (Map<Vector2, uint32_t>::Element *E = bitmask_map_copy.front(); E; E = E->next()) {
1790 undo_redo->add_do_method(tileset.ptr(), "autotile_set_bitmask", get_current_tile(), E->key(), E->value());
1791 }
1792 for (Map<Vector2, uint32_t>::Element *E = tileset->autotile_get_bitmask_map(get_current_tile()).front(); E; E = E->next()) {
1793 undo_redo->add_undo_method(tileset.ptr(), "autotile_set_bitmask", get_current_tile(), E->key(), E->value());
1794 }
1795 undo_redo->add_do_method(workspace, "update");
1796 undo_redo->add_undo_method(workspace, "update");
1797 undo_redo->commit_action();
1798 } else if (p_tool == BITMASK_CLEAR) {
1799 undo_redo->create_action(TTR("Clear Tile Bitmask"));
1800 undo_redo->add_do_method(tileset.ptr(), "autotile_clear_bitmask_map", get_current_tile());
1801 for (Map<Vector2, uint32_t>::Element *E = tileset->autotile_get_bitmask_map(get_current_tile()).front(); E; E = E->next()) {
1802 undo_redo->add_undo_method(tileset.ptr(), "autotile_set_bitmask", get_current_tile(), E->key(), E->value());
1803 }
1804 undo_redo->add_do_method(workspace, "update");
1805 undo_redo->add_undo_method(workspace, "update");
1806 undo_redo->commit_action();
1807 } else if (p_tool == SHAPE_TOGGLE_TYPE) {
1808 if (edited_collision_shape.is_valid()) {
1809 Ref<ConvexPolygonShape2D> convex = edited_collision_shape;
1810 Ref<ConcavePolygonShape2D> concave = edited_collision_shape;
1811 Ref<Shape2D> previous_shape = edited_collision_shape;
1812 Array sd = tileset->call("tile_get_shapes", get_current_tile());
1813
1814 if (convex.is_valid()) {
1815 // Make concave.
1816 undo_redo->create_action(TTR("Make Polygon Concave"));
1817 Ref<ConcavePolygonShape2D> _concave = memnew(ConcavePolygonShape2D);
1818 edited_collision_shape = _concave;
1819 _set_edited_shape_points(_get_collision_shape_points(convex));
1820 } else if (concave.is_valid()) {
1821 // Make convex.
1822 undo_redo->create_action(TTR("Make Polygon Convex"));
1823 Ref<ConvexPolygonShape2D> _convex = memnew(ConvexPolygonShape2D);
1824 edited_collision_shape = _convex;
1825 _set_edited_shape_points(_get_collision_shape_points(concave));
1826 }
1827 for (int i = 0; i < sd.size(); i++) {
1828 if (sd[i].get("shape") == previous_shape) {
1829 undo_redo->add_undo_method(tileset.ptr(), "tile_set_shapes", get_current_tile(), sd.duplicate());
1830 sd.remove(i);
1831 break;
1832 }
1833 }
1834
1835 undo_redo->add_do_method(tileset.ptr(), "tile_set_shapes", get_current_tile(), sd);
1836 if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE || tileset->tile_get_tile_mode(get_current_tile()) == TileSet::ATLAS_TILE) {
1837 undo_redo->add_do_method(tileset.ptr(), "tile_add_shape", get_current_tile(), edited_collision_shape, Transform2D(), false, edited_shape_coord);
1838 } else {
1839 undo_redo->add_do_method(tileset.ptr(), "tile_add_shape", get_current_tile(), edited_collision_shape, Transform2D());
1840 }
1841 undo_redo->add_do_method(this, "_select_edited_shape_coord");
1842 undo_redo->add_undo_method(this, "_select_edited_shape_coord");
1843 undo_redo->commit_action();
1844
1845 _update_toggle_shape_button();
1846 workspace->update();
1847 workspace_container->update();
1848 helper->_change_notify("");
1849 }
1850 } else if (p_tool == SELECT_NEXT) {
1851 _select_next_shape();
1852 } else if (p_tool == SELECT_PREVIOUS) {
1853 _select_previous_shape();
1854 } else if (p_tool == SHAPE_DELETE) {
1855 if (creating_shape) {
1856 creating_shape = false;
1857 current_shape.resize(0);
1858 workspace->update();
1859 } else {
1860 switch (edit_mode) {
1861 case EDITMODE_REGION: {
1862 int t_id = get_current_tile();
1863 if (workspace_mode == WORKSPACE_EDIT && t_id >= 0) {
1864 undo_redo->create_action(TTR("Remove Tile"));
1865 undo_redo->add_do_method(tileset.ptr(), "remove_tile", t_id);
1866 _undo_tile_removal(t_id);
1867 undo_redo->add_do_method(this, "_validate_current_tile_id");
1868
1869 Rect2 tile_region = tileset->tile_get_region(get_current_tile());
1870 Size2 tile_workspace_size = tile_region.position + tile_region.size;
1871 if (tile_workspace_size.x > get_current_texture()->get_size().x || tile_workspace_size.y > get_current_texture()->get_size().y) {
1872 undo_redo->add_do_method(this, "update_workspace_minsize");
1873 undo_redo->add_undo_method(this, "update_workspace_minsize");
1874 }
1875
1876 undo_redo->add_do_method(workspace, "update");
1877 undo_redo->add_undo_method(workspace, "update");
1878 undo_redo->add_do_method(workspace_overlay, "update");
1879 undo_redo->add_undo_method(workspace_overlay, "update");
1880 undo_redo->commit_action();
1881 }
1882 tool_workspacemode[WORKSPACE_EDIT]->set_pressed(true);
1883 workspace_mode = WORKSPACE_EDIT;
1884 update_workspace_tile_mode();
1885 } break;
1886 case EDITMODE_COLLISION: {
1887 if (!edited_collision_shape.is_null()) {
1888 // Necessary to get the version that returns a Array instead of a Vector.
1889 Array sd = tileset->call("tile_get_shapes", get_current_tile());
1890 for (int i = 0; i < sd.size(); i++) {
1891 if (sd[i].get("shape") == edited_collision_shape) {
1892 undo_redo->create_action(TTR("Remove Collision Polygon"));
1893 undo_redo->add_undo_method(tileset.ptr(), "tile_set_shapes", get_current_tile(), sd.duplicate());
1894 sd.remove(i);
1895 undo_redo->add_do_method(tileset.ptr(), "tile_set_shapes", get_current_tile(), sd);
1896 undo_redo->add_do_method(this, "_select_edited_shape_coord");
1897 undo_redo->add_undo_method(this, "_select_edited_shape_coord");
1898 undo_redo->commit_action();
1899 break;
1900 }
1901 }
1902 }
1903 } break;
1904 case EDITMODE_OCCLUSION: {
1905 if (!edited_occlusion_shape.is_null()) {
1906 undo_redo->create_action(TTR("Remove Occlusion Polygon"));
1907 if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE) {
1908 undo_redo->add_do_method(tileset.ptr(), "tile_set_light_occluder", get_current_tile(), Ref<OccluderPolygon2D>());
1909 undo_redo->add_undo_method(tileset.ptr(), "tile_set_light_occluder", get_current_tile(), tileset->tile_get_light_occluder(get_current_tile()));
1910 } else {
1911 undo_redo->add_do_method(tileset.ptr(), "autotile_set_light_occluder", get_current_tile(), Ref<OccluderPolygon2D>(), edited_shape_coord);
1912 undo_redo->add_undo_method(tileset.ptr(), "autotile_set_light_occluder", get_current_tile(), tileset->autotile_get_light_occluder(get_current_tile(), edited_shape_coord), edited_shape_coord);
1913 }
1914 undo_redo->add_do_method(this, "_select_edited_shape_coord");
1915 undo_redo->add_undo_method(this, "_select_edited_shape_coord");
1916 undo_redo->commit_action();
1917 }
1918 } break;
1919 case EDITMODE_NAVIGATION: {
1920 if (!edited_navigation_shape.is_null()) {
1921 undo_redo->create_action(TTR("Remove Navigation Polygon"));
1922 if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE) {
1923 undo_redo->add_do_method(tileset.ptr(), "tile_set_navigation_polygon", get_current_tile(), Ref<NavigationPolygon>());
1924 undo_redo->add_undo_method(tileset.ptr(), "tile_set_navigation_polygon", get_current_tile(), tileset->tile_get_navigation_polygon(get_current_tile()));
1925 } else {
1926 undo_redo->add_do_method(tileset.ptr(), "autotile_set_navigation_polygon", get_current_tile(), Ref<NavigationPolygon>(), edited_shape_coord);
1927 undo_redo->add_undo_method(tileset.ptr(), "autotile_set_navigation_polygon", get_current_tile(), tileset->autotile_get_navigation_polygon(get_current_tile(), edited_shape_coord), edited_shape_coord);
1928 }
1929 undo_redo->add_do_method(this, "_select_edited_shape_coord");
1930 undo_redo->add_undo_method(this, "_select_edited_shape_coord");
1931 undo_redo->commit_action();
1932 }
1933 } break;
1934 default: {
1935 }
1936 }
1937 }
1938 } else if (p_tool == TOOL_SELECT || p_tool == SHAPE_NEW_POLYGON || p_tool == SHAPE_NEW_RECTANGLE) {
1939 if (creating_shape) {
1940 // Cancel Creation
1941 creating_shape = false;
1942 current_shape.resize(0);
1943 workspace->update();
1944 }
1945 }
1946 }
1947
_on_priority_changed(float val)1948 void TileSetEditor::_on_priority_changed(float val) {
1949 if ((int)val == tileset->autotile_get_subtile_priority(get_current_tile(), edited_shape_coord))
1950 return;
1951
1952 undo_redo->create_action(TTR("Edit Tile Priority"));
1953 undo_redo->add_do_method(tileset.ptr(), "autotile_set_subtile_priority", get_current_tile(), edited_shape_coord, (int)val);
1954 undo_redo->add_undo_method(tileset.ptr(), "autotile_set_subtile_priority", get_current_tile(), edited_shape_coord, tileset->autotile_get_subtile_priority(get_current_tile(), edited_shape_coord));
1955 undo_redo->add_do_method(workspace, "update");
1956 undo_redo->add_undo_method(workspace, "update");
1957 undo_redo->commit_action();
1958 }
1959
_on_z_index_changed(float val)1960 void TileSetEditor::_on_z_index_changed(float val) {
1961 if ((int)val == tileset->autotile_get_z_index(get_current_tile(), edited_shape_coord))
1962 return;
1963
1964 undo_redo->create_action(TTR("Edit Tile Z Index"));
1965 undo_redo->add_do_method(tileset.ptr(), "autotile_set_z_index", get_current_tile(), edited_shape_coord, (int)val);
1966 undo_redo->add_undo_method(tileset.ptr(), "autotile_set_z_index", get_current_tile(), edited_shape_coord, tileset->autotile_get_z_index(get_current_tile(), edited_shape_coord));
1967 undo_redo->add_do_method(workspace, "update");
1968 undo_redo->add_undo_method(workspace, "update");
1969 undo_redo->commit_action();
1970 }
1971
_on_grid_snap_toggled(bool p_val)1972 void TileSetEditor::_on_grid_snap_toggled(bool p_val) {
1973 helper->set_snap_options_visible(p_val);
1974 workspace->update();
1975 }
1976
_get_collision_shape_points(const Ref<Shape2D> & p_shape)1977 Vector<Vector2> TileSetEditor::_get_collision_shape_points(const Ref<Shape2D> &p_shape) {
1978 Ref<ConvexPolygonShape2D> convex = p_shape;
1979 Ref<ConcavePolygonShape2D> concave = p_shape;
1980 if (convex.is_valid()) {
1981 return convex->get_points();
1982 } else if (concave.is_valid()) {
1983 Vector<Vector2> points;
1984 for (int i = 0; i < concave->get_segments().size(); i += 2) {
1985 points.push_back(concave->get_segments()[i]);
1986 }
1987 return points;
1988 } else {
1989 return Vector<Vector2>();
1990 }
1991 }
1992
_get_edited_shape_points()1993 Vector<Vector2> TileSetEditor::_get_edited_shape_points() {
1994 return _get_collision_shape_points(edited_collision_shape);
1995 }
1996
_set_edited_shape_points(const Vector<Vector2> & points)1997 void TileSetEditor::_set_edited_shape_points(const Vector<Vector2> &points) {
1998 Ref<ConvexPolygonShape2D> convex = edited_collision_shape;
1999 Ref<ConcavePolygonShape2D> concave = edited_collision_shape;
2000 if (convex.is_valid()) {
2001 undo_redo->add_do_method(convex.ptr(), "set_points", points);
2002 undo_redo->add_undo_method(convex.ptr(), "set_points", _get_edited_shape_points());
2003 } else if (concave.is_valid() && points.size() > 1) {
2004 PoolVector2Array segments;
2005 for (int i = 0; i < points.size() - 1; i++) {
2006 segments.push_back(points[i]);
2007 segments.push_back(points[i + 1]);
2008 }
2009 segments.push_back(points[points.size() - 1]);
2010 segments.push_back(points[0]);
2011 undo_redo->add_do_method(concave.ptr(), "set_segments", segments);
2012 undo_redo->add_undo_method(concave.ptr(), "set_segments", concave->get_segments());
2013 }
2014 }
2015
_update_tile_data()2016 void TileSetEditor::_update_tile_data() {
2017 current_tile_data.clear();
2018 if (get_current_tile() < 0)
2019 return;
2020
2021 Vector<TileSet::ShapeData> sd = tileset->tile_get_shapes(get_current_tile());
2022 if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE) {
2023 SubtileData data;
2024 for (int i = 0; i < sd.size(); i++) {
2025 data.collisions.push_back(sd[i].shape);
2026 }
2027 data.navigation_shape = tileset->tile_get_navigation_polygon(get_current_tile());
2028 data.occlusion_shape = tileset->tile_get_light_occluder(get_current_tile());
2029 current_tile_data[Vector2i()] = data;
2030 } else {
2031 int spacing = tileset->autotile_get_spacing(get_current_tile());
2032 Vector2 size = tileset->tile_get_region(get_current_tile()).size;
2033 Vector2 cell_count = (size / (tileset->autotile_get_size(get_current_tile()) + Vector2(spacing, spacing))).floor();
2034 for (int y = 0; y < cell_count.y; y++) {
2035 for (int x = 0; x < cell_count.x; x++) {
2036 SubtileData data;
2037 Vector2i coord(x, y);
2038 for (int i = 0; i < sd.size(); i++) {
2039 if (sd[i].autotile_coord == coord) {
2040 data.collisions.push_back(sd[i].shape);
2041 }
2042 }
2043 data.navigation_shape = tileset->autotile_get_navigation_polygon(get_current_tile(), coord);
2044 data.occlusion_shape = tileset->tile_get_light_occluder(get_current_tile());
2045 current_tile_data[coord] = data;
2046 }
2047 }
2048 }
2049 }
2050
_update_toggle_shape_button()2051 void TileSetEditor::_update_toggle_shape_button() {
2052 Ref<ConvexPolygonShape2D> convex = edited_collision_shape;
2053 Ref<ConcavePolygonShape2D> concave = edited_collision_shape;
2054 separator_shape_toggle->show();
2055 tools[SHAPE_TOGGLE_TYPE]->show();
2056 if (edit_mode != EDITMODE_COLLISION || !edited_collision_shape.is_valid()) {
2057 separator_shape_toggle->hide();
2058 tools[SHAPE_TOGGLE_TYPE]->hide();
2059 } else if (concave.is_valid()) {
2060 tools[SHAPE_TOGGLE_TYPE]->set_icon(get_icon("ConvexPolygonShape2D", "EditorIcons"));
2061 tools[SHAPE_TOGGLE_TYPE]->set_text(TTR("Make Convex"));
2062 } else if (convex.is_valid()) {
2063 tools[SHAPE_TOGGLE_TYPE]->set_icon(get_icon("ConcavePolygonShape2D", "EditorIcons"));
2064 tools[SHAPE_TOGGLE_TYPE]->set_text(TTR("Make Concave"));
2065 } else {
2066 // Shouldn't happen
2067 separator_shape_toggle->hide();
2068 tools[SHAPE_TOGGLE_TYPE]->hide();
2069 }
2070 }
2071
_select_next_tile()2072 void TileSetEditor::_select_next_tile() {
2073 Array tiles = _get_tiles_in_current_texture(true);
2074 if (tiles.size() == 0) {
2075 set_current_tile(-1);
2076 } else if (get_current_tile() == -1) {
2077 set_current_tile(tiles[0]);
2078 } else {
2079 int index = tiles.find(get_current_tile());
2080 if (index < 0) {
2081 set_current_tile(tiles[0]);
2082 } else if (index == tiles.size() - 1) {
2083 set_current_tile(tiles[0]);
2084 } else {
2085 set_current_tile(tiles[index + 1]);
2086 }
2087 }
2088 if (get_current_tile() == -1) {
2089 return;
2090 } else if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE) {
2091 return;
2092 } else {
2093 switch (edit_mode) {
2094 case EDITMODE_COLLISION:
2095 case EDITMODE_OCCLUSION:
2096 case EDITMODE_NAVIGATION:
2097 case EDITMODE_PRIORITY:
2098 case EDITMODE_Z_INDEX: {
2099 edited_shape_coord = Vector2();
2100 _select_edited_shape_coord();
2101 } break;
2102 default: {
2103 }
2104 }
2105 }
2106 }
2107
_select_previous_tile()2108 void TileSetEditor::_select_previous_tile() {
2109 Array tiles = _get_tiles_in_current_texture(true);
2110 if (tiles.size() == 0) {
2111 set_current_tile(-1);
2112 } else if (get_current_tile() == -1) {
2113 set_current_tile(tiles[tiles.size() - 1]);
2114 } else {
2115 int index = tiles.find(get_current_tile());
2116 if (index <= 0) {
2117 set_current_tile(tiles[tiles.size() - 1]);
2118 } else {
2119 set_current_tile(tiles[index - 1]);
2120 }
2121 }
2122 if (get_current_tile() == -1) {
2123 return;
2124 } else if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE) {
2125 return;
2126 } else {
2127 switch (edit_mode) {
2128 case EDITMODE_COLLISION:
2129 case EDITMODE_OCCLUSION:
2130 case EDITMODE_NAVIGATION:
2131 case EDITMODE_PRIORITY:
2132 case EDITMODE_Z_INDEX: {
2133 int spacing = tileset->autotile_get_spacing(get_current_tile());
2134 Vector2 size = tileset->tile_get_region(get_current_tile()).size;
2135 Vector2 cell_count = (size / (tileset->autotile_get_size(get_current_tile()) + Vector2(spacing, spacing))).floor();
2136 cell_count -= Vector2(1, 1);
2137 edited_shape_coord = cell_count;
2138 _select_edited_shape_coord();
2139 } break;
2140 default: {
2141 }
2142 }
2143 }
2144 }
2145
_get_tiles_in_current_texture(bool sorted)2146 Array TileSetEditor::_get_tiles_in_current_texture(bool sorted) {
2147 Array a;
2148 List<int> all_tiles;
2149 if (!get_current_texture().is_valid()) {
2150 return a;
2151 }
2152 tileset->get_tile_list(&all_tiles);
2153 for (int i = 0; i < all_tiles.size(); i++) {
2154 if (tileset->tile_get_texture(all_tiles[i]) == get_current_texture()) {
2155 a.push_back(all_tiles[i]);
2156 }
2157 }
2158 if (sorted) {
2159 a.sort_custom(this, "_sort_tiles");
2160 }
2161 return a;
2162 }
2163
_sort_tiles(Variant p_a,Variant p_b)2164 bool TileSetEditor::_sort_tiles(Variant p_a, Variant p_b) {
2165 int a = p_a;
2166 int b = p_b;
2167
2168 Vector2 pos_a = tileset->tile_get_region(a).position;
2169 Vector2 pos_b = tileset->tile_get_region(b).position;
2170 if (pos_a.y < pos_b.y) {
2171 return true;
2172
2173 } else if (pos_a.y == pos_b.y) {
2174 return (pos_a.x < pos_b.x);
2175 } else {
2176 return false;
2177 }
2178 }
2179
_select_next_subtile()2180 void TileSetEditor::_select_next_subtile() {
2181 if (get_current_tile() == -1) {
2182 _select_next_tile();
2183 return;
2184 }
2185 if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE) {
2186 _select_next_tile();
2187 } else if (edit_mode == EDITMODE_REGION || edit_mode == EDITMODE_BITMASK || edit_mode == EDITMODE_ICON) {
2188 _select_next_tile();
2189 } else {
2190 int spacing = tileset->autotile_get_spacing(get_current_tile());
2191 Vector2 size = tileset->tile_get_region(get_current_tile()).size;
2192 Vector2 cell_count = (size / (tileset->autotile_get_size(get_current_tile()) + Vector2(spacing, spacing))).floor();
2193 if (edited_shape_coord.x >= cell_count.x - 1 && edited_shape_coord.y >= cell_count.y - 1) {
2194 _select_next_tile();
2195 } else {
2196 edited_shape_coord.x++;
2197 if (edited_shape_coord.x >= cell_count.x) {
2198 edited_shape_coord.x = 0;
2199 edited_shape_coord.y++;
2200 }
2201 _select_edited_shape_coord();
2202 }
2203 }
2204 }
2205
_select_previous_subtile()2206 void TileSetEditor::_select_previous_subtile() {
2207 if (get_current_tile() == -1) {
2208 _select_previous_tile();
2209 return;
2210 }
2211 if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE) {
2212 _select_previous_tile();
2213 } else if (edit_mode == EDITMODE_REGION || edit_mode == EDITMODE_BITMASK || edit_mode == EDITMODE_ICON) {
2214 _select_previous_tile();
2215 } else {
2216 int spacing = tileset->autotile_get_spacing(get_current_tile());
2217 Vector2 size = tileset->tile_get_region(get_current_tile()).size;
2218 Vector2 cell_count = (size / (tileset->autotile_get_size(get_current_tile()) + Vector2(spacing, spacing))).floor();
2219 if (edited_shape_coord.x <= 0 && edited_shape_coord.y <= 0) {
2220 _select_previous_tile();
2221 } else {
2222 edited_shape_coord.x--;
2223 if (edited_shape_coord.x == -1) {
2224 edited_shape_coord.x = cell_count.x - 1;
2225 edited_shape_coord.y--;
2226 }
2227 _select_edited_shape_coord();
2228 }
2229 }
2230 }
2231
_select_next_shape()2232 void TileSetEditor::_select_next_shape() {
2233 if (get_current_tile() == -1) {
2234 _select_next_subtile();
2235 } else if (edit_mode != EDITMODE_COLLISION) {
2236 _select_next_subtile();
2237 } else {
2238 Vector2i edited_coord = Vector2i();
2239 if (tileset->tile_get_tile_mode(get_current_tile()) != TileSet::SINGLE_TILE) {
2240 edited_coord = Vector2i(edited_shape_coord);
2241 }
2242 SubtileData data = current_tile_data[edited_coord];
2243 if (data.collisions.size() == 0) {
2244 _select_next_subtile();
2245 } else {
2246 int index = data.collisions.find(edited_collision_shape);
2247 if (index < 0) {
2248 _set_edited_collision_shape(data.collisions[0]);
2249 } else if (index == data.collisions.size() - 1) {
2250 _select_next_subtile();
2251 } else {
2252 _set_edited_collision_shape(data.collisions[index + 1]);
2253 }
2254 }
2255 current_shape.resize(0);
2256 Rect2 current_tile_region = tileset->tile_get_region(get_current_tile());
2257 current_tile_region.position += WORKSPACE_MARGIN;
2258
2259 int spacing = tileset->autotile_get_spacing(get_current_tile());
2260 Vector2 size = tileset->autotile_get_size(get_current_tile());
2261 Vector2 shape_anchor = edited_shape_coord;
2262 shape_anchor.x *= (size.x + spacing);
2263 shape_anchor.y *= (size.y + spacing);
2264 current_tile_region.position += shape_anchor;
2265
2266 if (edited_collision_shape.is_valid()) {
2267 for (int i = 0; i < _get_edited_shape_points().size(); i++) {
2268 current_shape.push_back(_get_edited_shape_points()[i] + current_tile_region.position);
2269 }
2270 }
2271 workspace->update();
2272 workspace_container->update();
2273 helper->_change_notify("");
2274 }
2275 }
2276
_select_previous_shape()2277 void TileSetEditor::_select_previous_shape() {
2278 if (get_current_tile() == -1) {
2279 _select_previous_subtile();
2280 if (get_current_tile() != -1 && edit_mode == EDITMODE_COLLISION) {
2281 SubtileData data = current_tile_data[Vector2i(edited_shape_coord)];
2282 if (data.collisions.size() > 1) {
2283 _set_edited_collision_shape(data.collisions[data.collisions.size() - 1]);
2284 }
2285 } else {
2286 return;
2287 }
2288 } else if (edit_mode != EDITMODE_COLLISION) {
2289 _select_previous_subtile();
2290 } else {
2291 Vector2i edited_coord = Vector2i();
2292 if (tileset->tile_get_tile_mode(get_current_tile()) != TileSet::SINGLE_TILE) {
2293 edited_coord = Vector2i(edited_shape_coord);
2294 }
2295 SubtileData data = current_tile_data[edited_coord];
2296 if (data.collisions.size() == 0) {
2297 _select_previous_subtile();
2298 data = current_tile_data[Vector2i(edited_shape_coord)];
2299 if (data.collisions.size() > 1) {
2300 _set_edited_collision_shape(data.collisions[data.collisions.size() - 1]);
2301 }
2302 } else {
2303 int index = data.collisions.find(edited_collision_shape);
2304 if (index < 0) {
2305 _set_edited_collision_shape(data.collisions[data.collisions.size() - 1]);
2306 } else if (index == 0) {
2307 _select_previous_subtile();
2308 data = current_tile_data[Vector2i(edited_shape_coord)];
2309 if (data.collisions.size() > 1) {
2310 _set_edited_collision_shape(data.collisions[data.collisions.size() - 1]);
2311 }
2312 } else {
2313 _set_edited_collision_shape(data.collisions[index - 1]);
2314 }
2315 }
2316
2317 current_shape.resize(0);
2318 Rect2 current_tile_region = tileset->tile_get_region(get_current_tile());
2319 current_tile_region.position += WORKSPACE_MARGIN;
2320
2321 int spacing = tileset->autotile_get_spacing(get_current_tile());
2322 Vector2 size = tileset->autotile_get_size(get_current_tile());
2323 Vector2 shape_anchor = edited_shape_coord;
2324 shape_anchor.x *= (size.x + spacing);
2325 shape_anchor.y *= (size.y + spacing);
2326 current_tile_region.position += shape_anchor;
2327
2328 if (edited_collision_shape.is_valid()) {
2329 for (int i = 0; i < _get_edited_shape_points().size(); i++) {
2330 current_shape.push_back(_get_edited_shape_points()[i] + current_tile_region.position);
2331 }
2332 }
2333 workspace->update();
2334 workspace_container->update();
2335 helper->_change_notify("");
2336 }
2337 }
2338
_set_edited_collision_shape(const Ref<Shape2D> & p_shape)2339 void TileSetEditor::_set_edited_collision_shape(const Ref<Shape2D> &p_shape) {
2340 edited_collision_shape = p_shape;
2341 _update_toggle_shape_button();
2342 }
2343
_set_snap_step(Vector2 p_val)2344 void TileSetEditor::_set_snap_step(Vector2 p_val) {
2345 snap_step.x = CLAMP(p_val.x, 0, 256);
2346 snap_step.y = CLAMP(p_val.y, 0, 256);
2347 workspace->update();
2348 }
2349
_set_snap_off(Vector2 p_val)2350 void TileSetEditor::_set_snap_off(Vector2 p_val) {
2351 snap_offset.x = CLAMP(p_val.x, 0, 256 + WORKSPACE_MARGIN.x);
2352 snap_offset.y = CLAMP(p_val.y, 0, 256 + WORKSPACE_MARGIN.y);
2353 workspace->update();
2354 }
2355
_set_snap_sep(Vector2 p_val)2356 void TileSetEditor::_set_snap_sep(Vector2 p_val) {
2357 snap_separation.x = CLAMP(p_val.x, 0, 256);
2358 snap_separation.y = CLAMP(p_val.y, 0, 256);
2359 workspace->update();
2360 }
2361
_validate_current_tile_id()2362 void TileSetEditor::_validate_current_tile_id() {
2363 if (get_current_tile() >= 0 && !tileset->has_tile(get_current_tile()))
2364 set_current_tile(-1);
2365 }
2366
_select_edited_shape_coord()2367 void TileSetEditor::_select_edited_shape_coord() {
2368 select_coord(edited_shape_coord);
2369 }
2370
_undo_tile_removal(int p_id)2371 void TileSetEditor::_undo_tile_removal(int p_id) {
2372 undo_redo->add_undo_method(tileset.ptr(), "create_tile", p_id);
2373 undo_redo->add_undo_method(tileset.ptr(), "tile_set_name", p_id, tileset->tile_get_name(p_id));
2374 undo_redo->add_undo_method(tileset.ptr(), "tile_set_normal_map", p_id, tileset->tile_get_normal_map(p_id));
2375 undo_redo->add_undo_method(tileset.ptr(), "tile_set_texture_offset", p_id, tileset->tile_get_texture_offset(p_id));
2376 undo_redo->add_undo_method(tileset.ptr(), "tile_set_material", p_id, tileset->tile_get_material(p_id));
2377 undo_redo->add_undo_method(tileset.ptr(), "tile_set_modulate", p_id, tileset->tile_get_modulate(p_id));
2378 undo_redo->add_undo_method(tileset.ptr(), "tile_set_occluder_offset", p_id, tileset->tile_get_occluder_offset(p_id));
2379 undo_redo->add_undo_method(tileset.ptr(), "tile_set_navigation_polygon_offset", p_id, tileset->tile_get_navigation_polygon_offset(p_id));
2380 undo_redo->add_undo_method(tileset.ptr(), "tile_set_shape_offset", p_id, 0, tileset->tile_get_shape_offset(p_id, 0));
2381 undo_redo->add_undo_method(tileset.ptr(), "tile_set_shape_transform", p_id, 0, tileset->tile_get_shape_transform(p_id, 0));
2382 undo_redo->add_undo_method(tileset.ptr(), "tile_set_z_index", p_id, tileset->tile_get_z_index(p_id));
2383 undo_redo->add_undo_method(tileset.ptr(), "tile_set_texture", p_id, tileset->tile_get_texture(p_id));
2384 undo_redo->add_undo_method(tileset.ptr(), "tile_set_region", p_id, tileset->tile_get_region(p_id));
2385 // Necessary to get the version that returns a Array instead of a Vector.
2386 undo_redo->add_undo_method(tileset.ptr(), "tile_set_shapes", p_id, tileset->call("tile_get_shapes", p_id));
2387 if (tileset->tile_get_tile_mode(p_id) == TileSet::SINGLE_TILE) {
2388 undo_redo->add_undo_method(tileset.ptr(), "tile_set_light_occluder", p_id, tileset->tile_get_light_occluder(p_id));
2389 undo_redo->add_undo_method(tileset.ptr(), "tile_set_navigation_polygon", p_id, tileset->tile_get_navigation_polygon(p_id));
2390 } else {
2391 Map<Vector2, Ref<OccluderPolygon2D> > oclusion_map = tileset->autotile_get_light_oclusion_map(p_id);
2392 for (Map<Vector2, Ref<OccluderPolygon2D> >::Element *E = oclusion_map.front(); E; E = E->next()) {
2393 undo_redo->add_undo_method(tileset.ptr(), "autotile_set_light_occluder", p_id, E->value(), E->key());
2394 }
2395 Map<Vector2, Ref<NavigationPolygon> > navigation_map = tileset->autotile_get_navigation_map(p_id);
2396 for (Map<Vector2, Ref<NavigationPolygon> >::Element *E = navigation_map.front(); E; E = E->next()) {
2397 undo_redo->add_undo_method(tileset.ptr(), "autotile_set_navigation_polygon", p_id, E->value(), E->key());
2398 }
2399 Map<Vector2, uint32_t> bitmask_map = tileset->autotile_get_bitmask_map(p_id);
2400 for (Map<Vector2, uint32_t>::Element *E = bitmask_map.front(); E; E = E->next()) {
2401 undo_redo->add_undo_method(tileset.ptr(), "autotile_set_bitmask", p_id, E->key(), E->value());
2402 }
2403 Map<Vector2, int> priority_map = tileset->autotile_get_priority_map(p_id);
2404 for (Map<Vector2, int>::Element *E = priority_map.front(); E; E = E->next()) {
2405 undo_redo->add_undo_method(tileset.ptr(), "autotile_set_subtile_priority", p_id, E->key(), E->value());
2406 }
2407 undo_redo->add_undo_method(tileset.ptr(), "autotile_set_icon_coordinate", p_id, tileset->autotile_get_icon_coordinate(p_id));
2408 Map<Vector2, int> z_map = tileset->autotile_get_z_index_map(p_id);
2409 for (Map<Vector2, int>::Element *E = z_map.front(); E; E = E->next()) {
2410 undo_redo->add_undo_method(tileset.ptr(), "autotile_set_z_index", p_id, E->key(), E->value());
2411 }
2412 undo_redo->add_undo_method(tileset.ptr(), "tile_set_tile_mode", p_id, tileset->tile_get_tile_mode(p_id));
2413 undo_redo->add_undo_method(tileset.ptr(), "autotile_set_size", p_id, tileset->autotile_get_size(p_id));
2414 undo_redo->add_undo_method(tileset.ptr(), "autotile_set_spacing", p_id, tileset->autotile_get_spacing(p_id));
2415 undo_redo->add_undo_method(tileset.ptr(), "autotile_set_bitmask_mode", p_id, tileset->autotile_get_bitmask_mode(p_id));
2416 }
2417 }
2418
_zoom_in()2419 void TileSetEditor::_zoom_in() {
2420 float scale = workspace->get_scale().x;
2421 if (scale < max_scale) {
2422 scale *= scale_ratio;
2423 workspace->set_scale(Vector2(scale, scale));
2424 workspace_container->set_custom_minimum_size(workspace->get_rect().size * scale);
2425 workspace_overlay->set_custom_minimum_size(workspace->get_rect().size * scale);
2426 }
2427 }
_zoom_out()2428 void TileSetEditor::_zoom_out() {
2429 float scale = workspace->get_scale().x;
2430 if (scale > min_scale) {
2431 scale /= scale_ratio;
2432 workspace->set_scale(Vector2(scale, scale));
2433 workspace_container->set_custom_minimum_size(workspace->get_rect().size * scale);
2434 workspace_overlay->set_custom_minimum_size(workspace->get_rect().size * scale);
2435 }
2436 }
_zoom_reset()2437 void TileSetEditor::_zoom_reset() {
2438 workspace->set_scale(Vector2(1, 1));
2439 workspace_container->set_custom_minimum_size(workspace->get_rect().size);
2440 workspace_overlay->set_custom_minimum_size(workspace->get_rect().size);
2441 }
2442
draw_highlight_current_tile()2443 void TileSetEditor::draw_highlight_current_tile() {
2444
2445 Color shadow_color = Color(0.3, 0.3, 0.3, 0.3);
2446 if ((workspace_mode == WORKSPACE_EDIT && get_current_tile() >= 0) || !edited_region.has_no_area()) {
2447 Rect2 region;
2448 if (edited_region.has_no_area()) {
2449 region = tileset->tile_get_region(get_current_tile());
2450 region.position += WORKSPACE_MARGIN;
2451 } else {
2452 region = edited_region;
2453 }
2454
2455 if (region.position.y >= 0)
2456 workspace->draw_rect(Rect2(0, 0, workspace->get_rect().size.x, region.position.y), shadow_color);
2457 if (region.position.x >= 0)
2458 workspace->draw_rect(Rect2(0, MAX(0, region.position.y), region.position.x, MIN(workspace->get_rect().size.y - region.position.y, MIN(region.size.y, region.position.y + region.size.y))), shadow_color);
2459 if (region.position.x + region.size.x <= workspace->get_rect().size.x)
2460 workspace->draw_rect(Rect2(region.position.x + region.size.x, MAX(0, region.position.y), workspace->get_rect().size.x - region.position.x - region.size.x, MIN(workspace->get_rect().size.y - region.position.y, MIN(region.size.y, region.position.y + region.size.y))), shadow_color);
2461 if (region.position.y + region.size.y <= workspace->get_rect().size.y)
2462 workspace->draw_rect(Rect2(0, region.position.y + region.size.y, workspace->get_rect().size.x, workspace->get_rect().size.y - region.size.y - region.position.y), shadow_color);
2463 } else {
2464 workspace->draw_rect(Rect2(Point2(0, 0), workspace->get_rect().size), shadow_color);
2465 }
2466 }
2467
draw_highlight_subtile(Vector2 coord,const Vector<Vector2> & other_highlighted)2468 void TileSetEditor::draw_highlight_subtile(Vector2 coord, const Vector<Vector2> &other_highlighted) {
2469
2470 Color shadow_color = Color(0.3, 0.3, 0.3, 0.3);
2471 Vector2 size = tileset->autotile_get_size(get_current_tile());
2472 int spacing = tileset->autotile_get_spacing(get_current_tile());
2473 Rect2 region = tileset->tile_get_region(get_current_tile());
2474 coord.x *= (size.x + spacing);
2475 coord.y *= (size.y + spacing);
2476 coord += region.position;
2477 coord += WORKSPACE_MARGIN;
2478
2479 if (coord.y >= 0)
2480 workspace->draw_rect(Rect2(0, 0, workspace->get_rect().size.x, coord.y), shadow_color);
2481 if (coord.x >= 0)
2482 workspace->draw_rect(Rect2(0, MAX(0, coord.y), coord.x, MIN(workspace->get_rect().size.y - coord.y, MIN(size.y, coord.y + size.y))), shadow_color);
2483 if (coord.x + size.x <= workspace->get_rect().size.x)
2484 workspace->draw_rect(Rect2(coord.x + size.x, MAX(0, coord.y), workspace->get_rect().size.x - coord.x - size.x, MIN(workspace->get_rect().size.y - coord.y, MIN(size.y, coord.y + size.y))), shadow_color);
2485 if (coord.y + size.y <= workspace->get_rect().size.y)
2486 workspace->draw_rect(Rect2(0, coord.y + size.y, workspace->get_rect().size.x, workspace->get_rect().size.y - size.y - coord.y), shadow_color);
2487
2488 coord += Vector2(1, 1) / workspace->get_scale().x;
2489 workspace->draw_rect(Rect2(coord, size - Vector2(2, 2) / workspace->get_scale().x), Color(1, 0, 0), false);
2490 for (int i = 0; i < other_highlighted.size(); i++) {
2491 coord = other_highlighted[i];
2492 coord.x *= (size.x + spacing);
2493 coord.y *= (size.y + spacing);
2494 coord += region.position;
2495 coord += WORKSPACE_MARGIN;
2496 coord += Vector2(1, 1) / workspace->get_scale().x;
2497 workspace->draw_rect(Rect2(coord, size - Vector2(2, 2) / workspace->get_scale().x), Color(1, 0.5, 0.5), false);
2498 }
2499 }
2500
draw_tile_subdivision(int p_id,Color p_color) const2501 void TileSetEditor::draw_tile_subdivision(int p_id, Color p_color) const {
2502 Color c = p_color;
2503 if (tileset->tile_get_tile_mode(p_id) == TileSet::AUTO_TILE || tileset->tile_get_tile_mode(p_id) == TileSet::ATLAS_TILE) {
2504 Rect2 region = tileset->tile_get_region(p_id);
2505 Size2 size = tileset->autotile_get_size(p_id);
2506 int spacing = tileset->autotile_get_spacing(p_id);
2507 float j = size.x;
2508
2509 while (j < region.size.x) {
2510 if (spacing <= 0) {
2511 workspace->draw_line(region.position + WORKSPACE_MARGIN + Point2(j, 0), region.position + WORKSPACE_MARGIN + Point2(j, region.size.y), c);
2512 } else {
2513 workspace->draw_rect(Rect2(region.position + WORKSPACE_MARGIN + Point2(j, 0), Size2(spacing, region.size.y)), c);
2514 }
2515 j += spacing + size.x;
2516 }
2517 j = size.y;
2518 while (j < region.size.y) {
2519 if (spacing <= 0) {
2520 workspace->draw_line(region.position + WORKSPACE_MARGIN + Point2(0, j), region.position + WORKSPACE_MARGIN + Point2(region.size.x, j), c);
2521 } else {
2522 workspace->draw_rect(Rect2(region.position + WORKSPACE_MARGIN + Point2(0, j), Size2(region.size.x, spacing)), c);
2523 }
2524 j += spacing + size.y;
2525 }
2526 }
2527 }
2528
draw_edited_region_subdivision() const2529 void TileSetEditor::draw_edited_region_subdivision() const {
2530 Color c = Color(0.3, 0.7, 0.6);
2531 Rect2 region = edited_region;
2532 Size2 size;
2533 int spacing;
2534 bool draw;
2535
2536 if (workspace_mode == WORKSPACE_EDIT) {
2537 int p_id = get_current_tile();
2538 size = tileset->autotile_get_size(p_id);
2539 spacing = tileset->autotile_get_spacing(p_id);
2540 draw = tileset->tile_get_tile_mode(p_id) == TileSet::AUTO_TILE || tileset->tile_get_tile_mode(p_id) == TileSet::ATLAS_TILE;
2541 } else {
2542 size = snap_step;
2543 spacing = snap_separation.x;
2544 draw = workspace_mode != WORKSPACE_CREATE_SINGLE;
2545 }
2546
2547 if (draw) {
2548 float j = size.x;
2549 while (j < region.size.x) {
2550 if (spacing <= 0) {
2551 workspace->draw_line(region.position + Point2(j, 0), region.position + Point2(j, region.size.y), c);
2552 } else {
2553 workspace->draw_rect(Rect2(region.position + Point2(j, 0), Size2(spacing, region.size.y)), c);
2554 }
2555 j += spacing + size.x;
2556 }
2557 j = size.y;
2558 while (j < region.size.y) {
2559 if (spacing <= 0) {
2560 workspace->draw_line(region.position + Point2(0, j), region.position + Point2(region.size.x, j), c);
2561 } else {
2562 workspace->draw_rect(Rect2(region.position + Point2(0, j), Size2(region.size.x, spacing)), c);
2563 }
2564 j += spacing + size.y;
2565 }
2566 }
2567 }
2568
draw_grid_snap()2569 void TileSetEditor::draw_grid_snap() {
2570 if (tools[TOOL_GRID_SNAP]->is_pressed()) {
2571 Color grid_color = Color(0.4, 0, 1);
2572 Size2 s = workspace->get_size();
2573
2574 int width_count = Math::floor((s.width - WORKSPACE_MARGIN.x) / (snap_step.x + snap_separation.x));
2575 int height_count = Math::floor((s.height - WORKSPACE_MARGIN.y) / (snap_step.y + snap_separation.y));
2576
2577 int last_p = 0;
2578 if (snap_step.x != 0) {
2579 for (int i = 0; i <= width_count; i++) {
2580 if (i == 0 && snap_offset.x != 0) {
2581 last_p = snap_offset.x;
2582 }
2583 if (snap_separation.x != 0) {
2584 if (i != 0) {
2585 workspace->draw_rect(Rect2(last_p, 0, snap_separation.x, s.height), grid_color);
2586 last_p += snap_separation.x;
2587 } else {
2588 workspace->draw_rect(Rect2(last_p, 0, -snap_separation.x, s.height), grid_color);
2589 }
2590 } else {
2591 workspace->draw_line(Point2(last_p, 0), Point2(last_p, s.height), grid_color);
2592 }
2593 last_p += snap_step.x;
2594 }
2595 }
2596 last_p = 0;
2597 if (snap_step.y != 0) {
2598 for (int i = 0; i <= height_count; i++) {
2599 if (i == 0 && snap_offset.y != 0) {
2600 last_p = snap_offset.y;
2601 }
2602 if (snap_separation.y != 0) {
2603 if (i != 0) {
2604 workspace->draw_rect(Rect2(0, last_p, s.width, snap_separation.y), grid_color);
2605 last_p += snap_separation.y;
2606 } else {
2607 workspace->draw_rect(Rect2(0, last_p, s.width, -snap_separation.y), grid_color);
2608 }
2609 } else {
2610 workspace->draw_line(Point2(0, last_p), Point2(s.width, last_p), grid_color);
2611 }
2612 last_p += snap_step.y;
2613 }
2614 }
2615 }
2616 }
2617
draw_polygon_shapes()2618 void TileSetEditor::draw_polygon_shapes() {
2619
2620 int t_id = get_current_tile();
2621 if (t_id < 0)
2622 return;
2623
2624 switch (edit_mode) {
2625 case EDITMODE_COLLISION: {
2626 Vector<TileSet::ShapeData> sd = tileset->tile_get_shapes(t_id);
2627 for (int i = 0; i < sd.size(); i++) {
2628 Vector2 coord = Vector2(0, 0);
2629 Vector2 anchor = Vector2(0, 0);
2630 if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE || tileset->tile_get_tile_mode(get_current_tile()) == TileSet::ATLAS_TILE) {
2631 coord = sd[i].autotile_coord;
2632 anchor = tileset->autotile_get_size(t_id);
2633 anchor.x += tileset->autotile_get_spacing(t_id);
2634 anchor.y += tileset->autotile_get_spacing(t_id);
2635 anchor.x *= coord.x;
2636 anchor.y *= coord.y;
2637 }
2638 anchor += WORKSPACE_MARGIN;
2639 anchor += tileset->tile_get_region(t_id).position;
2640 Ref<Shape2D> shape = sd[i].shape;
2641 if (shape.is_valid()) {
2642 Color c_bg;
2643 Color c_border;
2644 Ref<ConvexPolygonShape2D> convex = shape;
2645 bool is_convex = convex.is_valid();
2646 if ((tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE || coord == edited_shape_coord) && sd[i].shape == edited_collision_shape) {
2647 if (is_convex) {
2648 c_bg = Color(0, 1, 1, 0.5);
2649 c_border = Color(0, 1, 1);
2650 } else {
2651 c_bg = Color(0.8, 0, 1, 0.5);
2652 c_border = Color(0.8, 0, 1);
2653 }
2654 } else {
2655 if (is_convex) {
2656 c_bg = Color(0.9, 0.7, 0.07, 0.5);
2657 c_border = Color(0.9, 0.7, 0.07, 1);
2658
2659 } else {
2660 c_bg = Color(0.9, 0.45, 0.075, 0.5);
2661 c_border = Color(0.9, 0.45, 0.075);
2662 }
2663 }
2664 Vector<Vector2> polygon;
2665 Vector<Color> colors;
2666 if (!creating_shape && shape == edited_collision_shape && current_shape.size() > 2) {
2667 for (int j = 0; j < current_shape.size(); j++) {
2668 polygon.push_back(current_shape[j]);
2669 colors.push_back(c_bg);
2670 }
2671 } else {
2672 for (int j = 0; j < _get_collision_shape_points(shape).size(); j++) {
2673 polygon.push_back(_get_collision_shape_points(shape)[j] + anchor);
2674 colors.push_back(c_bg);
2675 }
2676 }
2677
2678 if (polygon.size() < 3)
2679 continue;
2680
2681 workspace->draw_polygon(polygon, colors);
2682
2683 if (coord == edited_shape_coord || tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE) {
2684 if (!creating_shape && polygon.size() > 1) {
2685 for (int j = 0; j < polygon.size() - 1; j++) {
2686 workspace->draw_line(polygon[j], polygon[j + 1], c_border, 1, true);
2687 }
2688 workspace->draw_line(polygon[polygon.size() - 1], polygon[0], c_border, 1, true);
2689 }
2690 if (shape == edited_collision_shape) {
2691 draw_handles = true;
2692 }
2693 }
2694 }
2695 }
2696 } break;
2697 case EDITMODE_OCCLUSION: {
2698 if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE) {
2699 Ref<OccluderPolygon2D> shape = edited_occlusion_shape;
2700 if (shape.is_valid()) {
2701 Color c_bg = Color(0, 1, 1, 0.5);
2702 Color c_border = Color(0, 1, 1);
2703
2704 Vector<Vector2> polygon;
2705 Vector<Color> colors;
2706 Vector2 anchor = WORKSPACE_MARGIN;
2707 anchor += tileset->tile_get_region(get_current_tile()).position;
2708 if (!creating_shape && shape == edited_occlusion_shape && current_shape.size() > 2) {
2709 for (int j = 0; j < current_shape.size(); j++) {
2710 polygon.push_back(current_shape[j]);
2711 colors.push_back(c_bg);
2712 }
2713 } else {
2714 for (int j = 0; j < shape->get_polygon().size(); j++) {
2715 polygon.push_back(shape->get_polygon()[j] + anchor);
2716 colors.push_back(c_bg);
2717 }
2718 }
2719 workspace->draw_polygon(polygon, colors);
2720
2721 if (!creating_shape && polygon.size() > 1) {
2722 for (int j = 0; j < polygon.size() - 1; j++) {
2723 workspace->draw_line(polygon[j], polygon[j + 1], c_border, 1, true);
2724 }
2725 workspace->draw_line(polygon[polygon.size() - 1], polygon[0], c_border, 1, true);
2726 }
2727 if (shape == edited_occlusion_shape) {
2728 draw_handles = true;
2729 }
2730 }
2731 } else {
2732 Map<Vector2, Ref<OccluderPolygon2D> > map = tileset->autotile_get_light_oclusion_map(t_id);
2733 for (Map<Vector2, Ref<OccluderPolygon2D> >::Element *E = map.front(); E; E = E->next()) {
2734 Vector2 coord = E->key();
2735 Vector2 anchor = tileset->autotile_get_size(t_id);
2736 anchor.x += tileset->autotile_get_spacing(t_id);
2737 anchor.y += tileset->autotile_get_spacing(t_id);
2738 anchor.x *= coord.x;
2739 anchor.y *= coord.y;
2740 anchor += WORKSPACE_MARGIN;
2741 anchor += tileset->tile_get_region(t_id).position;
2742 Ref<OccluderPolygon2D> shape = E->value();
2743 if (shape.is_valid()) {
2744 Color c_bg;
2745 Color c_border;
2746 if (coord == edited_shape_coord && shape == edited_occlusion_shape) {
2747 c_bg = Color(0, 1, 1, 0.5);
2748 c_border = Color(0, 1, 1);
2749 } else {
2750 c_bg = Color(0.9, 0.7, 0.07, 0.5);
2751 c_border = Color(0.9, 0.7, 0.07, 1);
2752 }
2753 Vector<Vector2> polygon;
2754 Vector<Color> colors;
2755 if (!creating_shape && shape == edited_occlusion_shape && current_shape.size() > 2) {
2756 for (int j = 0; j < current_shape.size(); j++) {
2757 polygon.push_back(current_shape[j]);
2758 colors.push_back(c_bg);
2759 }
2760 } else {
2761 for (int j = 0; j < shape->get_polygon().size(); j++) {
2762 polygon.push_back(shape->get_polygon()[j] + anchor);
2763 colors.push_back(c_bg);
2764 }
2765 }
2766 workspace->draw_polygon(polygon, colors);
2767
2768 if (coord == edited_shape_coord) {
2769 if (!creating_shape && polygon.size() > 1) {
2770 for (int j = 0; j < polygon.size() - 1; j++) {
2771 workspace->draw_line(polygon[j], polygon[j + 1], c_border, 1, true);
2772 }
2773 workspace->draw_line(polygon[polygon.size() - 1], polygon[0], c_border, 1, true);
2774 }
2775 if (shape == edited_occlusion_shape) {
2776 draw_handles = true;
2777 }
2778 }
2779 }
2780 }
2781 }
2782 } break;
2783 case EDITMODE_NAVIGATION: {
2784 if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE) {
2785 Ref<NavigationPolygon> shape = edited_navigation_shape;
2786
2787 if (shape.is_valid()) {
2788 Color c_bg = Color(0, 1, 1, 0.5);
2789 Color c_border = Color(0, 1, 1);
2790
2791 Vector<Vector2> polygon;
2792 Vector<Color> colors;
2793 Vector2 anchor = WORKSPACE_MARGIN;
2794 anchor += tileset->tile_get_region(get_current_tile()).position;
2795 if (!creating_shape && shape == edited_navigation_shape && current_shape.size() > 2) {
2796 for (int j = 0; j < current_shape.size(); j++) {
2797 polygon.push_back(current_shape[j]);
2798 colors.push_back(c_bg);
2799 }
2800 } else {
2801 PoolVector<Vector2> vertices = shape->get_vertices();
2802 for (int j = 0; j < shape->get_polygon(0).size(); j++) {
2803 polygon.push_back(vertices[shape->get_polygon(0)[j]] + anchor);
2804 colors.push_back(c_bg);
2805 }
2806 }
2807 workspace->draw_polygon(polygon, colors);
2808
2809 if (!creating_shape && polygon.size() > 1) {
2810 for (int j = 0; j < polygon.size() - 1; j++) {
2811 workspace->draw_line(polygon[j], polygon[j + 1], c_border, 1, true);
2812 }
2813 workspace->draw_line(polygon[polygon.size() - 1], polygon[0], c_border, 1, true);
2814 }
2815 if (shape == edited_navigation_shape) {
2816 draw_handles = true;
2817 }
2818 }
2819 } else {
2820 Map<Vector2, Ref<NavigationPolygon> > map = tileset->autotile_get_navigation_map(t_id);
2821 for (Map<Vector2, Ref<NavigationPolygon> >::Element *E = map.front(); E; E = E->next()) {
2822 Vector2 coord = E->key();
2823 Vector2 anchor = tileset->autotile_get_size(t_id);
2824 anchor.x += tileset->autotile_get_spacing(t_id);
2825 anchor.y += tileset->autotile_get_spacing(t_id);
2826 anchor.x *= coord.x;
2827 anchor.y *= coord.y;
2828 anchor += WORKSPACE_MARGIN;
2829 anchor += tileset->tile_get_region(t_id).position;
2830 Ref<NavigationPolygon> shape = E->value();
2831 if (shape.is_valid()) {
2832 Color c_bg;
2833 Color c_border;
2834 if (coord == edited_shape_coord && shape == edited_navigation_shape) {
2835 c_bg = Color(0, 1, 1, 0.5);
2836 c_border = Color(0, 1, 1);
2837 } else {
2838 c_bg = Color(0.9, 0.7, 0.07, 0.5);
2839 c_border = Color(0.9, 0.7, 0.07, 1);
2840 }
2841 Vector<Vector2> polygon;
2842 Vector<Color> colors;
2843 if (!creating_shape && shape == edited_navigation_shape && current_shape.size() > 2) {
2844 for (int j = 0; j < current_shape.size(); j++) {
2845 polygon.push_back(current_shape[j]);
2846 colors.push_back(c_bg);
2847 }
2848 } else {
2849 PoolVector<Vector2> vertices = shape->get_vertices();
2850 for (int j = 0; j < shape->get_polygon(0).size(); j++) {
2851 polygon.push_back(vertices[shape->get_polygon(0)[j]] + anchor);
2852 colors.push_back(c_bg);
2853 }
2854 }
2855 workspace->draw_polygon(polygon, colors);
2856
2857 if (coord == edited_shape_coord) {
2858 if (!creating_shape && polygon.size() > 1) {
2859 for (int j = 0; j < polygon.size() - 1; j++) {
2860 workspace->draw_line(polygon[j], polygon[j + 1], c_border, 1, true);
2861 }
2862 workspace->draw_line(polygon[polygon.size() - 1], polygon[0], c_border, 1, true);
2863 }
2864 if (shape == edited_navigation_shape) {
2865 draw_handles = true;
2866 }
2867 }
2868 }
2869 }
2870 }
2871 } break;
2872 default: {
2873 }
2874 }
2875
2876 if (creating_shape && current_shape.size() > 1) {
2877 for (int j = 0; j < current_shape.size() - 1; j++) {
2878 workspace->draw_line(current_shape[j], current_shape[j + 1], Color(0, 1, 1), 1, true);
2879 }
2880 workspace->draw_line(current_shape[current_shape.size() - 1], snap_point(workspace->get_local_mouse_position()), Color(0, 1, 1), 1, true);
2881 draw_handles = true;
2882 }
2883 }
2884
close_shape(const Vector2 & shape_anchor)2885 void TileSetEditor::close_shape(const Vector2 &shape_anchor) {
2886
2887 creating_shape = false;
2888
2889 if (edit_mode == EDITMODE_COLLISION) {
2890 if (current_shape.size() >= 3) {
2891 Ref<ConvexPolygonShape2D> shape = memnew(ConvexPolygonShape2D);
2892
2893 Vector<Vector2> points;
2894 float p_total = 0;
2895
2896 for (int i = 0; i < current_shape.size(); i++) {
2897 points.push_back(current_shape[i] - shape_anchor);
2898
2899 if (i != current_shape.size() - 1)
2900 p_total += ((current_shape[i + 1].x - current_shape[i].x) * (-current_shape[i + 1].y + (-current_shape[i].y)));
2901 else
2902 p_total += ((current_shape[0].x - current_shape[i].x) * (-current_shape[0].y + (-current_shape[i].y)));
2903 }
2904
2905 if (p_total < 0)
2906 points.invert();
2907
2908 shape->set_points(points);
2909
2910 undo_redo->create_action(TTR("Create Collision Polygon"));
2911 // Necessary to get the version that returns a Array instead of a Vector.
2912 Array sd = tileset->call("tile_get_shapes", get_current_tile());
2913 undo_redo->add_undo_method(tileset.ptr(), "tile_set_shapes", get_current_tile(), sd.duplicate());
2914 for (int i = 0; i < sd.size(); i++) {
2915 if (sd[i].get("shape") == edited_collision_shape) {
2916 sd.remove(i);
2917 break;
2918 }
2919 }
2920 undo_redo->add_do_method(tileset.ptr(), "tile_set_shapes", get_current_tile(), sd);
2921 if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE || tileset->tile_get_tile_mode(get_current_tile()) == TileSet::ATLAS_TILE)
2922 undo_redo->add_do_method(tileset.ptr(), "tile_add_shape", get_current_tile(), shape, Transform2D(), false, edited_shape_coord);
2923 else
2924 undo_redo->add_do_method(tileset.ptr(), "tile_add_shape", get_current_tile(), shape, Transform2D());
2925 tools[TOOL_SELECT]->set_pressed(true);
2926 undo_redo->add_do_method(this, "_select_edited_shape_coord");
2927 undo_redo->add_undo_method(this, "_select_edited_shape_coord");
2928 undo_redo->commit_action();
2929 } else {
2930 tools[TOOL_SELECT]->set_pressed(true);
2931 workspace->update();
2932 }
2933 } else if (edit_mode == EDITMODE_OCCLUSION) {
2934 Ref<OccluderPolygon2D> shape = memnew(OccluderPolygon2D);
2935
2936 PoolVector<Vector2> polygon;
2937 polygon.resize(current_shape.size());
2938 PoolVector<Vector2>::Write w = polygon.write();
2939
2940 for (int i = 0; i < current_shape.size(); i++) {
2941 w[i] = current_shape[i] - shape_anchor;
2942 }
2943
2944 w.release();
2945 shape->set_polygon(polygon);
2946
2947 undo_redo->create_action(TTR("Create Occlusion Polygon"));
2948 if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE || tileset->tile_get_tile_mode(get_current_tile()) == TileSet::ATLAS_TILE) {
2949 undo_redo->add_do_method(tileset.ptr(), "autotile_set_light_occluder", get_current_tile(), shape, edited_shape_coord);
2950 undo_redo->add_undo_method(tileset.ptr(), "autotile_set_light_occluder", get_current_tile(), tileset->autotile_get_light_occluder(get_current_tile(), edited_shape_coord), edited_shape_coord);
2951 } else {
2952 undo_redo->add_do_method(tileset.ptr(), "tile_set_light_occluder", get_current_tile(), shape);
2953 undo_redo->add_undo_method(tileset.ptr(), "tile_set_light_occluder", get_current_tile(), tileset->tile_get_light_occluder(get_current_tile()));
2954 }
2955 tools[TOOL_SELECT]->set_pressed(true);
2956 undo_redo->add_do_method(this, "_select_edited_shape_coord");
2957 undo_redo->add_undo_method(this, "_select_edited_shape_coord");
2958 undo_redo->commit_action();
2959 } else if (edit_mode == EDITMODE_NAVIGATION) {
2960 Ref<NavigationPolygon> shape = memnew(NavigationPolygon);
2961
2962 PoolVector<Vector2> polygon;
2963 Vector<int> indices;
2964 polygon.resize(current_shape.size());
2965 PoolVector<Vector2>::Write w = polygon.write();
2966
2967 for (int i = 0; i < current_shape.size(); i++) {
2968 w[i] = current_shape[i] - shape_anchor;
2969 indices.push_back(i);
2970 }
2971
2972 w.release();
2973 shape->set_vertices(polygon);
2974 shape->add_polygon(indices);
2975
2976 undo_redo->create_action(TTR("Create Navigation Polygon"));
2977 if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE || tileset->tile_get_tile_mode(get_current_tile()) == TileSet::ATLAS_TILE) {
2978 undo_redo->add_do_method(tileset.ptr(), "autotile_set_navigation_polygon", get_current_tile(), shape, edited_shape_coord);
2979 undo_redo->add_undo_method(tileset.ptr(), "autotile_set_navigation_polygon", get_current_tile(), tileset->autotile_get_navigation_polygon(get_current_tile(), edited_shape_coord), edited_shape_coord);
2980 } else {
2981 undo_redo->add_do_method(tileset.ptr(), "tile_set_navigation_polygon", get_current_tile(), shape);
2982 undo_redo->add_undo_method(tileset.ptr(), "tile_set_navigation_polygon", get_current_tile(), tileset->tile_get_navigation_polygon(get_current_tile()));
2983 }
2984 tools[TOOL_SELECT]->set_pressed(true);
2985 undo_redo->add_do_method(this, "_select_edited_shape_coord");
2986 undo_redo->add_undo_method(this, "_select_edited_shape_coord");
2987 undo_redo->commit_action();
2988 }
2989 tileset->_change_notify("");
2990 }
2991
select_coord(const Vector2 & coord)2992 void TileSetEditor::select_coord(const Vector2 &coord) {
2993 _update_tile_data();
2994 current_shape = PoolVector2Array();
2995 if (get_current_tile() == -1)
2996 return;
2997 Rect2 current_tile_region = tileset->tile_get_region(get_current_tile());
2998 current_tile_region.position += WORKSPACE_MARGIN;
2999 if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE) {
3000 if (edited_collision_shape != tileset->tile_get_shape(get_current_tile(), 0))
3001 _set_edited_collision_shape(tileset->tile_get_shape(get_current_tile(), 0));
3002 if (edited_occlusion_shape != tileset->tile_get_light_occluder(get_current_tile()))
3003 edited_occlusion_shape = tileset->tile_get_light_occluder(get_current_tile());
3004 if (edited_navigation_shape != tileset->tile_get_navigation_polygon(get_current_tile()))
3005 edited_navigation_shape = tileset->tile_get_navigation_polygon(get_current_tile());
3006
3007 if (edit_mode == EDITMODE_COLLISION) {
3008 current_shape.resize(0);
3009 if (edited_collision_shape.is_valid()) {
3010 for (int i = 0; i < _get_edited_shape_points().size(); i++) {
3011 current_shape.push_back(_get_edited_shape_points()[i] + current_tile_region.position);
3012 }
3013 }
3014 } else if (edit_mode == EDITMODE_OCCLUSION) {
3015 current_shape.resize(0);
3016 if (edited_occlusion_shape.is_valid()) {
3017 for (int i = 0; i < edited_occlusion_shape->get_polygon().size(); i++) {
3018 current_shape.push_back(edited_occlusion_shape->get_polygon()[i] + current_tile_region.position);
3019 }
3020 }
3021 } else if (edit_mode == EDITMODE_NAVIGATION) {
3022 current_shape.resize(0);
3023 if (edited_navigation_shape.is_valid()) {
3024 if (edited_navigation_shape->get_polygon_count() > 0) {
3025 PoolVector<Vector2> vertices = edited_navigation_shape->get_vertices();
3026 for (int i = 0; i < edited_navigation_shape->get_polygon(0).size(); i++) {
3027 current_shape.push_back(vertices[edited_navigation_shape->get_polygon(0)[i]] + current_tile_region.position);
3028 }
3029 }
3030 }
3031 }
3032 } else {
3033 Vector<TileSet::ShapeData> sd = tileset->tile_get_shapes(get_current_tile());
3034 bool found_collision_shape = false;
3035 for (int i = 0; i < sd.size(); i++) {
3036 if (sd[i].autotile_coord == coord) {
3037 if (edited_collision_shape != sd[i].shape)
3038 _set_edited_collision_shape(sd[i].shape);
3039 found_collision_shape = true;
3040 break;
3041 }
3042 }
3043 if (!found_collision_shape)
3044 _set_edited_collision_shape(Ref<ConvexPolygonShape2D>(NULL));
3045 if (edited_occlusion_shape != tileset->autotile_get_light_occluder(get_current_tile(), coord))
3046 edited_occlusion_shape = tileset->autotile_get_light_occluder(get_current_tile(), coord);
3047 if (edited_navigation_shape != tileset->autotile_get_navigation_polygon(get_current_tile(), coord))
3048 edited_navigation_shape = tileset->autotile_get_navigation_polygon(get_current_tile(), coord);
3049
3050 int spacing = tileset->autotile_get_spacing(get_current_tile());
3051 Vector2 size = tileset->autotile_get_size(get_current_tile());
3052 Vector2 shape_anchor = coord;
3053 shape_anchor.x *= (size.x + spacing);
3054 shape_anchor.y *= (size.y + spacing);
3055 shape_anchor += current_tile_region.position;
3056 if (edit_mode == EDITMODE_COLLISION) {
3057 current_shape.resize(0);
3058 if (edited_collision_shape.is_valid()) {
3059 for (int j = 0; j < _get_edited_shape_points().size(); j++) {
3060 current_shape.push_back(_get_edited_shape_points()[j] + shape_anchor);
3061 }
3062 }
3063 } else if (edit_mode == EDITMODE_OCCLUSION) {
3064 current_shape.resize(0);
3065 if (edited_occlusion_shape.is_valid()) {
3066 for (int i = 0; i < edited_occlusion_shape->get_polygon().size(); i++) {
3067 current_shape.push_back(edited_occlusion_shape->get_polygon()[i] + shape_anchor);
3068 }
3069 }
3070 } else if (edit_mode == EDITMODE_NAVIGATION) {
3071 current_shape.resize(0);
3072 if (edited_navigation_shape.is_valid()) {
3073 if (edited_navigation_shape->get_polygon_count() > 0) {
3074 PoolVector<Vector2> vertices = edited_navigation_shape->get_vertices();
3075 for (int i = 0; i < edited_navigation_shape->get_polygon(0).size(); i++) {
3076 current_shape.push_back(vertices[edited_navigation_shape->get_polygon(0)[i]] + shape_anchor);
3077 }
3078 }
3079 }
3080 }
3081 }
3082 workspace->update();
3083 workspace_container->update();
3084 helper->_change_notify("");
3085 }
3086
snap_point(const Vector2 & point)3087 Vector2 TileSetEditor::snap_point(const Vector2 &point) {
3088 Vector2 p = point;
3089 Vector2 coord = edited_shape_coord;
3090 Vector2 tile_size = tileset->autotile_get_size(get_current_tile());
3091 int spacing = tileset->autotile_get_spacing(get_current_tile());
3092 Vector2 anchor = coord;
3093 anchor.x *= (tile_size.x + spacing);
3094 anchor.y *= (tile_size.y + spacing);
3095 anchor += tileset->tile_get_region(get_current_tile()).position;
3096 anchor += WORKSPACE_MARGIN;
3097 Rect2 region(anchor, tile_size);
3098 if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE) {
3099 region.position = tileset->tile_get_region(get_current_tile()).position + WORKSPACE_MARGIN;
3100 region.size = tileset->tile_get_region(get_current_tile()).size;
3101 }
3102
3103 if (tools[TOOL_GRID_SNAP]->is_pressed()) {
3104 p.x = Math::snap_scalar_separation(snap_offset.x, snap_step.x, p.x, snap_separation.x);
3105 p.y = Math::snap_scalar_separation(snap_offset.y, snap_step.y, p.y, snap_separation.y);
3106 }
3107 if (tools[SHAPE_KEEP_INSIDE_TILE]->is_pressed()) {
3108 if (p.x < region.position.x)
3109 p.x = region.position.x;
3110 if (p.y < region.position.y)
3111 p.y = region.position.y;
3112 if (p.x > region.position.x + region.size.x)
3113 p.x = region.position.x + region.size.x;
3114 if (p.y > region.position.y + region.size.y)
3115 p.y = region.position.y + region.size.y;
3116 }
3117 return p;
3118 }
3119
add_texture(Ref<Texture> p_texture)3120 void TileSetEditor::add_texture(Ref<Texture> p_texture) {
3121 texture_list->add_item(p_texture->get_path().get_file());
3122 texture_map.insert(p_texture->get_rid(), p_texture);
3123 texture_list->set_item_metadata(texture_list->get_item_count() - 1, p_texture->get_rid());
3124 }
3125
remove_texture(Ref<Texture> p_texture)3126 void TileSetEditor::remove_texture(Ref<Texture> p_texture) {
3127 texture_list->remove_item(texture_list->find_metadata(p_texture->get_rid()));
3128 texture_map.erase(p_texture->get_rid());
3129
3130 _validate_current_tile_id();
3131
3132 if (!get_current_texture().is_valid()) {
3133 _on_texture_list_selected(-1);
3134 workspace_overlay->update();
3135 }
3136 }
3137
update_texture_list()3138 void TileSetEditor::update_texture_list() {
3139 Ref<Texture> selected_texture = get_current_texture();
3140
3141 helper->set_tileset(tileset);
3142
3143 List<int> ids;
3144 tileset->get_tile_list(&ids);
3145 Vector<int> ids_to_remove;
3146 for (List<int>::Element *E = ids.front(); E; E = E->next()) {
3147 // Clear tiles referencing gone textures (user has been already given the chance to fix broken deps)
3148 if (!tileset->tile_get_texture(E->get()).is_valid()) {
3149 ids_to_remove.push_back(E->get());
3150 ERR_CONTINUE(!tileset->tile_get_texture(E->get()).is_valid());
3151 }
3152
3153 if (!texture_map.has(tileset->tile_get_texture(E->get())->get_rid())) {
3154 add_texture(tileset->tile_get_texture(E->get()));
3155 }
3156 }
3157 for (int i = 0; i < ids_to_remove.size(); i++) {
3158 tileset->remove_tile(ids_to_remove[i]);
3159 }
3160
3161 if (texture_list->get_item_count() > 0 && selected_texture.is_valid()) {
3162 texture_list->select(texture_list->find_metadata(selected_texture->get_rid()));
3163 if (texture_list->get_selected_items().size() > 0)
3164 _on_texture_list_selected(texture_list->get_selected_items()[0]);
3165 } else if (get_current_texture().is_valid()) {
3166 _on_texture_list_selected(texture_list->find_metadata(get_current_texture()->get_rid()));
3167 } else {
3168 _validate_current_tile_id();
3169 _on_texture_list_selected(-1);
3170 workspace_overlay->update();
3171 }
3172 update_texture_list_icon();
3173 helper->_change_notify("");
3174 }
3175
update_texture_list_icon()3176 void TileSetEditor::update_texture_list_icon() {
3177
3178 for (int current_idx = 0; current_idx < texture_list->get_item_count(); current_idx++) {
3179 RID rid = texture_list->get_item_metadata(current_idx);
3180 texture_list->set_item_icon(current_idx, texture_map[rid]);
3181 Size2 texture_size = texture_map[rid]->get_size();
3182 texture_list->set_item_icon_region(current_idx, Rect2(0, 0, MIN(texture_size.x, 150), MIN(texture_size.y, 100)));
3183 }
3184 texture_list->update();
3185 }
3186
update_workspace_tile_mode()3187 void TileSetEditor::update_workspace_tile_mode() {
3188
3189 if (!get_current_texture().is_valid()) {
3190 tool_workspacemode[WORKSPACE_EDIT]->set_pressed(true);
3191 workspace_mode = WORKSPACE_EDIT;
3192 for (int i = 1; i < WORKSPACE_MODE_MAX; i++) {
3193 tool_workspacemode[i]->set_disabled(true);
3194 }
3195 tools[SELECT_NEXT]->set_disabled(true);
3196 tools[SELECT_PREVIOUS]->set_disabled(true);
3197
3198 tools[ZOOM_OUT]->hide();
3199 tools[ZOOM_1]->hide();
3200 tools[ZOOM_IN]->hide();
3201 tools[VISIBLE_INFO]->hide();
3202
3203 scroll->hide();
3204 empty_message->show();
3205 } else {
3206 for (int i = 1; i < WORKSPACE_MODE_MAX; i++) {
3207 tool_workspacemode[i]->set_disabled(false);
3208 }
3209 tools[SELECT_NEXT]->set_disabled(false);
3210 tools[SELECT_PREVIOUS]->set_disabled(false);
3211
3212 tools[ZOOM_OUT]->show();
3213 tools[ZOOM_1]->show();
3214 tools[ZOOM_IN]->show();
3215 tools[VISIBLE_INFO]->show();
3216
3217 scroll->show();
3218 empty_message->hide();
3219 }
3220
3221 if (workspace_mode != WORKSPACE_EDIT) {
3222 for (int i = 0; i < EDITMODE_MAX; i++) {
3223 tool_editmode[i]->hide();
3224 }
3225 tool_editmode[EDITMODE_REGION]->show();
3226 tool_editmode[EDITMODE_REGION]->set_pressed(true);
3227 _on_edit_mode_changed(EDITMODE_REGION);
3228 separator_editmode->show();
3229 return;
3230 }
3231
3232 if (get_current_tile() < 0) {
3233 for (int i = 0; i < EDITMODE_MAX; i++) {
3234 tool_editmode[i]->hide();
3235 }
3236 for (int i = TOOL_SELECT; i < ZOOM_OUT; i++) {
3237 tools[i]->hide();
3238 }
3239
3240 separator_editmode->hide();
3241 separator_bitmask->hide();
3242 separator_delete->hide();
3243 separator_grid->hide();
3244 return;
3245 }
3246
3247 for (int i = 0; i < EDITMODE_MAX; i++) {
3248 tool_editmode[i]->show();
3249 }
3250 separator_editmode->show();
3251
3252 if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE) {
3253 if (tool_editmode[EDITMODE_ICON]->is_pressed() || tool_editmode[EDITMODE_PRIORITY]->is_pressed() || tool_editmode[EDITMODE_BITMASK]->is_pressed() || tool_editmode[EDITMODE_Z_INDEX]->is_pressed()) {
3254 tool_editmode[EDITMODE_COLLISION]->set_pressed(true);
3255 edit_mode = EDITMODE_COLLISION;
3256 }
3257 select_coord(Vector2(0, 0));
3258
3259 tool_editmode[EDITMODE_ICON]->hide();
3260 tool_editmode[EDITMODE_BITMASK]->hide();
3261 tool_editmode[EDITMODE_PRIORITY]->hide();
3262 tool_editmode[EDITMODE_Z_INDEX]->hide();
3263 } else if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE) {
3264 if (edit_mode == EDITMODE_ICON)
3265 select_coord(tileset->autotile_get_icon_coordinate(get_current_tile()));
3266 else
3267 _select_edited_shape_coord();
3268 } else if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::ATLAS_TILE) {
3269 if (tool_editmode[EDITMODE_PRIORITY]->is_pressed() || tool_editmode[EDITMODE_BITMASK]->is_pressed()) {
3270 tool_editmode[EDITMODE_COLLISION]->set_pressed(true);
3271 edit_mode = EDITMODE_COLLISION;
3272 }
3273 if (edit_mode == EDITMODE_ICON)
3274 select_coord(tileset->autotile_get_icon_coordinate(get_current_tile()));
3275 else
3276 _select_edited_shape_coord();
3277
3278 tool_editmode[EDITMODE_BITMASK]->hide();
3279 }
3280 _on_edit_mode_changed(edit_mode);
3281 }
3282
update_workspace_minsize()3283 void TileSetEditor::update_workspace_minsize() {
3284 Size2 workspace_min_size = get_current_texture()->get_size();
3285 RID current_texture_rid = get_current_texture()->get_rid();
3286 List<int> *tiles = new List<int>();
3287 tileset->get_tile_list(tiles);
3288 for (List<int>::Element *E = tiles->front(); E; E = E->next()) {
3289 if (tileset->tile_get_texture(E->get())->get_rid() != current_texture_rid) {
3290 continue;
3291 }
3292
3293 Rect2i region = tileset->tile_get_region(E->get());
3294 if (region.position.x + region.size.x > workspace_min_size.x) {
3295 workspace_min_size.x = region.position.x + region.size.x;
3296 }
3297 if (region.position.y + region.size.y > workspace_min_size.y) {
3298 workspace_min_size.y = region.position.y + region.size.y;
3299 }
3300 }
3301 delete tiles;
3302
3303 workspace->set_custom_minimum_size(workspace_min_size + WORKSPACE_MARGIN * 2);
3304 workspace_container->set_custom_minimum_size(workspace_min_size * workspace->get_scale() + WORKSPACE_MARGIN * 2);
3305 workspace_overlay->set_custom_minimum_size(workspace_min_size * workspace->get_scale() + WORKSPACE_MARGIN * 2);
3306 }
3307
update_edited_region(const Vector2 & end_point)3308 void TileSetEditor::update_edited_region(const Vector2 &end_point) {
3309 edited_region = Rect2(region_from, Size2());
3310 if (tools[TOOL_GRID_SNAP]->is_pressed()) {
3311 Vector2 grid_coord;
3312 grid_coord = ((region_from - snap_offset) / (snap_step + snap_separation)).floor();
3313 grid_coord *= (snap_step + snap_separation);
3314 grid_coord += snap_offset;
3315 edited_region.expand_to(grid_coord);
3316 grid_coord += snap_step;
3317 edited_region.expand_to(grid_coord);
3318
3319 grid_coord = ((end_point - snap_offset) / (snap_step + snap_separation)).floor();
3320 grid_coord *= (snap_step + snap_separation);
3321 grid_coord += snap_offset;
3322 edited_region.expand_to(grid_coord);
3323 grid_coord += snap_step;
3324 edited_region.expand_to(grid_coord);
3325 } else {
3326 edited_region.expand_to(end_point);
3327 }
3328 }
3329
get_current_tile() const3330 int TileSetEditor::get_current_tile() const {
3331 return current_tile;
3332 }
3333
set_current_tile(int p_id)3334 void TileSetEditor::set_current_tile(int p_id) {
3335 if (current_tile != p_id) {
3336 current_tile = p_id;
3337 helper->_change_notify("");
3338 select_coord(Vector2(0, 0));
3339 update_workspace_tile_mode();
3340 if (p_id == -1) {
3341 editor->get_inspector()->edit(tileset.ptr());
3342 } else {
3343 editor->get_inspector()->edit(helper);
3344 }
3345 }
3346 }
3347
get_current_texture()3348 Ref<Texture> TileSetEditor::get_current_texture() {
3349 if (texture_list->get_selected_items().size() == 0)
3350 return Ref<Texture>();
3351 else
3352 return texture_map[texture_list->get_item_metadata(texture_list->get_selected_items()[0])];
3353 }
3354
set_tileset(const Ref<TileSet> & p_tileset)3355 void TilesetEditorContext::set_tileset(const Ref<TileSet> &p_tileset) {
3356
3357 tileset = p_tileset;
3358 }
3359
set_snap_options_visible(bool p_visible)3360 void TilesetEditorContext::set_snap_options_visible(bool p_visible) {
3361 snap_options_visible = p_visible;
3362 _change_notify("");
3363 }
3364
_set(const StringName & p_name,const Variant & p_value)3365 bool TilesetEditorContext::_set(const StringName &p_name, const Variant &p_value) {
3366
3367 String name = p_name.operator String();
3368
3369 if (name == "options_offset") {
3370 Vector2 snap = p_value;
3371 tileset_editor->_set_snap_off(snap + WORKSPACE_MARGIN);
3372 return true;
3373 } else if (name == "options_step") {
3374 Vector2 snap = p_value;
3375 tileset_editor->_set_snap_step(snap);
3376 return true;
3377 } else if (name == "options_separation") {
3378 Vector2 snap = p_value;
3379 tileset_editor->_set_snap_sep(snap);
3380 return true;
3381 } else if (p_name.operator String().left(5) == "tile_") {
3382 String name2 = p_name.operator String().right(5);
3383 bool v = false;
3384
3385 if (tileset_editor->get_current_tile() < 0 || tileset.is_null())
3386 return false;
3387
3388 if (name2 == "autotile_bitmask_mode") {
3389 tileset->set(String::num(tileset_editor->get_current_tile(), 0) + "/autotile/bitmask_mode", p_value, &v);
3390 } else if (name2 == "subtile_size") {
3391 tileset->set(String::num(tileset_editor->get_current_tile(), 0) + "/autotile/tile_size", p_value, &v);
3392 } else if (name2 == "subtile_spacing") {
3393 tileset->set(String::num(tileset_editor->get_current_tile(), 0) + "/autotile/spacing", p_value, &v);
3394 } else {
3395 tileset->set(String::num(tileset_editor->get_current_tile(), 0) + "/" + name2, p_value, &v);
3396 }
3397 if (v) {
3398 tileset->_change_notify("");
3399 tileset_editor->workspace->update();
3400 tileset_editor->workspace_overlay->update();
3401 }
3402 return v;
3403 } else if (name == "tileset_script") {
3404 tileset->set_script(p_value);
3405 return true;
3406 } else if (name == "selected_collision_one_way") {
3407 Vector<TileSet::ShapeData> sd = tileset->tile_get_shapes(tileset_editor->get_current_tile());
3408 for (int index = 0; index < sd.size(); index++) {
3409 if (sd[index].shape == tileset_editor->edited_collision_shape) {
3410 tileset->tile_set_shape_one_way(tileset_editor->get_current_tile(), index, p_value);
3411 return true;
3412 }
3413 }
3414 return false;
3415 } else if (name == "selected_collision_one_way_margin") {
3416 Vector<TileSet::ShapeData> sd = tileset->tile_get_shapes(tileset_editor->get_current_tile());
3417 for (int index = 0; index < sd.size(); index++) {
3418 if (sd[index].shape == tileset_editor->edited_collision_shape) {
3419 tileset->tile_set_shape_one_way_margin(tileset_editor->get_current_tile(), index, p_value);
3420 return true;
3421 }
3422 }
3423 return false;
3424 }
3425
3426 tileset_editor->err_dialog->set_text(TTR("This property can't be changed."));
3427 tileset_editor->err_dialog->popup_centered(Size2(300, 60));
3428 return false;
3429 }
3430
_get(const StringName & p_name,Variant & r_ret) const3431 bool TilesetEditorContext::_get(const StringName &p_name, Variant &r_ret) const {
3432
3433 String name = p_name.operator String();
3434 bool v = false;
3435
3436 if (name == "options_offset") {
3437 r_ret = tileset_editor->snap_offset - WORKSPACE_MARGIN;
3438 v = true;
3439 } else if (name == "options_step") {
3440 r_ret = tileset_editor->snap_step;
3441 v = true;
3442 } else if (name == "options_separation") {
3443 r_ret = tileset_editor->snap_separation;
3444 v = true;
3445 } else if (name.left(5) == "tile_") {
3446 name = name.right(5);
3447
3448 if (tileset_editor->get_current_tile() < 0 || tileset.is_null())
3449 return false;
3450 if (!tileset->has_tile(tileset_editor->get_current_tile()))
3451 return false;
3452
3453 if (name == "autotile_bitmask_mode") {
3454 r_ret = tileset->get(String::num(tileset_editor->get_current_tile(), 0) + "/autotile/bitmask_mode", &v);
3455 } else if (name == "subtile_size") {
3456 r_ret = tileset->get(String::num(tileset_editor->get_current_tile(), 0) + "/autotile/tile_size", &v);
3457 } else if (name == "subtile_spacing") {
3458 r_ret = tileset->get(String::num(tileset_editor->get_current_tile(), 0) + "/autotile/spacing", &v);
3459 } else {
3460 r_ret = tileset->get(String::num(tileset_editor->get_current_tile(), 0) + "/" + name, &v);
3461 }
3462 return v;
3463 } else if (name == "selected_collision") {
3464 r_ret = tileset_editor->edited_collision_shape;
3465 v = true;
3466 } else if (name == "selected_collision_one_way") {
3467 Vector<TileSet::ShapeData> sd = tileset->tile_get_shapes(tileset_editor->get_current_tile());
3468 for (int index = 0; index < sd.size(); index++) {
3469 if (sd[index].shape == tileset_editor->edited_collision_shape) {
3470 r_ret = sd[index].one_way_collision;
3471 v = true;
3472 break;
3473 }
3474 }
3475 } else if (name == "selected_collision_one_way_margin") {
3476 Vector<TileSet::ShapeData> sd = tileset->tile_get_shapes(tileset_editor->get_current_tile());
3477 for (int index = 0; index < sd.size(); index++) {
3478 if (sd[index].shape == tileset_editor->edited_collision_shape) {
3479 r_ret = sd[index].one_way_collision_margin;
3480 v = true;
3481 break;
3482 }
3483 }
3484 } else if (name == "selected_navigation") {
3485 r_ret = tileset_editor->edited_navigation_shape;
3486 v = true;
3487 } else if (name == "selected_occlusion") {
3488 r_ret = tileset_editor->edited_occlusion_shape;
3489 v = true;
3490 } else if (name == "tileset_script") {
3491 r_ret = tileset->get_script();
3492 v = true;
3493 }
3494 return v;
3495 }
3496
_get_property_list(List<PropertyInfo> * p_list) const3497 void TilesetEditorContext::_get_property_list(List<PropertyInfo> *p_list) const {
3498
3499 if (snap_options_visible) {
3500 p_list->push_back(PropertyInfo(Variant::NIL, "Snap Options", PROPERTY_HINT_NONE, "options_", PROPERTY_USAGE_GROUP));
3501 p_list->push_back(PropertyInfo(Variant::VECTOR2, "options_offset"));
3502 p_list->push_back(PropertyInfo(Variant::VECTOR2, "options_step"));
3503 p_list->push_back(PropertyInfo(Variant::VECTOR2, "options_separation"));
3504 }
3505 if (tileset_editor->get_current_tile() >= 0 && !tileset.is_null()) {
3506 int id = tileset_editor->get_current_tile();
3507 p_list->push_back(PropertyInfo(Variant::NIL, "Selected Tile", PROPERTY_HINT_NONE, "tile_", PROPERTY_USAGE_GROUP));
3508 p_list->push_back(PropertyInfo(Variant::STRING, "tile_name"));
3509 p_list->push_back(PropertyInfo(Variant::OBJECT, "tile_normal_map", PROPERTY_HINT_RESOURCE_TYPE, "Texture"));
3510 p_list->push_back(PropertyInfo(Variant::VECTOR2, "tile_tex_offset"));
3511 p_list->push_back(PropertyInfo(Variant::OBJECT, "tile_material", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial"));
3512 p_list->push_back(PropertyInfo(Variant::COLOR, "tile_modulate"));
3513 p_list->push_back(PropertyInfo(Variant::INT, "tile_tile_mode", PROPERTY_HINT_ENUM, "SINGLE_TILE,AUTO_TILE,ATLAS_TILE"));
3514 if (tileset->tile_get_tile_mode(id) == TileSet::AUTO_TILE) {
3515 p_list->push_back(PropertyInfo(Variant::INT, "tile_autotile_bitmask_mode", PROPERTY_HINT_ENUM, "2X2,3X3 (minimal),3X3"));
3516 p_list->push_back(PropertyInfo(Variant::VECTOR2, "tile_subtile_size"));
3517 p_list->push_back(PropertyInfo(Variant::INT, "tile_subtile_spacing", PROPERTY_HINT_RANGE, "0, 256, 1"));
3518 } else if (tileset->tile_get_tile_mode(id) == TileSet::ATLAS_TILE) {
3519 p_list->push_back(PropertyInfo(Variant::VECTOR2, "tile_subtile_size"));
3520 p_list->push_back(PropertyInfo(Variant::INT, "tile_subtile_spacing", PROPERTY_HINT_RANGE, "0, 256, 1"));
3521 }
3522 p_list->push_back(PropertyInfo(Variant::VECTOR2, "tile_occluder_offset"));
3523 p_list->push_back(PropertyInfo(Variant::VECTOR2, "tile_navigation_offset"));
3524 p_list->push_back(PropertyInfo(Variant::VECTOR2, "tile_shape_offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR));
3525 p_list->push_back(PropertyInfo(Variant::VECTOR2, "tile_shape_transform", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR));
3526 p_list->push_back(PropertyInfo(Variant::INT, "tile_z_index", PROPERTY_HINT_RANGE, itos(VS::CANVAS_ITEM_Z_MIN) + "," + itos(VS::CANVAS_ITEM_Z_MAX) + ",1"));
3527 }
3528 if (tileset_editor->edit_mode == TileSetEditor::EDITMODE_COLLISION && tileset_editor->edited_collision_shape.is_valid()) {
3529 p_list->push_back(PropertyInfo(Variant::OBJECT, "selected_collision", PROPERTY_HINT_RESOURCE_TYPE, tileset_editor->edited_collision_shape->get_class()));
3530 if (tileset_editor->edited_collision_shape.is_valid()) {
3531 p_list->push_back(PropertyInfo(Variant::BOOL, "selected_collision_one_way", PROPERTY_HINT_NONE));
3532 p_list->push_back(PropertyInfo(Variant::REAL, "selected_collision_one_way_margin", PROPERTY_HINT_NONE));
3533 }
3534 }
3535 if (tileset_editor->edit_mode == TileSetEditor::EDITMODE_NAVIGATION && tileset_editor->edited_navigation_shape.is_valid()) {
3536 p_list->push_back(PropertyInfo(Variant::OBJECT, "selected_navigation", PROPERTY_HINT_RESOURCE_TYPE, tileset_editor->edited_navigation_shape->get_class()));
3537 }
3538 if (tileset_editor->edit_mode == TileSetEditor::EDITMODE_OCCLUSION && tileset_editor->edited_occlusion_shape.is_valid()) {
3539 p_list->push_back(PropertyInfo(Variant::OBJECT, "selected_occlusion", PROPERTY_HINT_RESOURCE_TYPE, tileset_editor->edited_occlusion_shape->get_class()));
3540 }
3541 if (!tileset.is_null()) {
3542 p_list->push_back(PropertyInfo(Variant::OBJECT, "tileset_script", PROPERTY_HINT_RESOURCE_TYPE, "Script"));
3543 }
3544 }
3545
_bind_methods()3546 void TilesetEditorContext::_bind_methods() {
3547
3548 ClassDB::bind_method("_hide_script_from_inspector", &TilesetEditorContext::_hide_script_from_inspector);
3549 }
3550
TilesetEditorContext(TileSetEditor * p_tileset_editor)3551 TilesetEditorContext::TilesetEditorContext(TileSetEditor *p_tileset_editor) {
3552
3553 tileset_editor = p_tileset_editor;
3554 snap_options_visible = false;
3555 }
3556
edit(Object * p_node)3557 void TileSetEditorPlugin::edit(Object *p_node) {
3558
3559 if (Object::cast_to<TileSet>(p_node)) {
3560 tileset_editor->edit(Object::cast_to<TileSet>(p_node));
3561 }
3562 }
3563
handles(Object * p_node) const3564 bool TileSetEditorPlugin::handles(Object *p_node) const {
3565
3566 return p_node->is_class("TileSet") || p_node->is_class("TilesetEditorContext");
3567 }
3568
make_visible(bool p_visible)3569 void TileSetEditorPlugin::make_visible(bool p_visible) {
3570 if (p_visible) {
3571 tileset_editor_button->show();
3572 editor->make_bottom_panel_item_visible(tileset_editor);
3573 get_tree()->connect("idle_frame", tileset_editor, "_on_workspace_process");
3574 } else {
3575 editor->hide_bottom_panel();
3576 tileset_editor_button->hide();
3577 get_tree()->disconnect("idle_frame", tileset_editor, "_on_workspace_process");
3578 }
3579 }
3580
get_state() const3581 Dictionary TileSetEditorPlugin::get_state() const {
3582
3583 Dictionary state;
3584 state["snap_offset"] = tileset_editor->snap_offset;
3585 state["snap_step"] = tileset_editor->snap_step;
3586 state["snap_separation"] = tileset_editor->snap_separation;
3587 state["snap_enabled"] = tileset_editor->tools[TileSetEditor::TOOL_GRID_SNAP]->is_pressed();
3588 state["keep_inside_tile"] = tileset_editor->tools[TileSetEditor::SHAPE_KEEP_INSIDE_TILE]->is_pressed();
3589 state["show_information"] = tileset_editor->tools[TileSetEditor::VISIBLE_INFO]->is_pressed();
3590 return state;
3591 }
3592
set_state(const Dictionary & p_state)3593 void TileSetEditorPlugin::set_state(const Dictionary &p_state) {
3594
3595 Dictionary state = p_state;
3596 if (state.has("snap_step")) {
3597 tileset_editor->_set_snap_step(state["snap_step"]);
3598 }
3599
3600 if (state.has("snap_offset")) {
3601 tileset_editor->_set_snap_off(state["snap_offset"]);
3602 }
3603
3604 if (state.has("snap_separation")) {
3605 tileset_editor->_set_snap_sep(state["snap_separation"]);
3606 }
3607
3608 if (state.has("snap_enabled")) {
3609 tileset_editor->tools[TileSetEditor::TOOL_GRID_SNAP]->set_pressed(state["snap_enabled"]);
3610 if (tileset_editor->helper) {
3611 tileset_editor->_on_grid_snap_toggled(state["snap_enabled"]);
3612 }
3613 }
3614
3615 if (state.has("keep_inside_tile")) {
3616 tileset_editor->tools[TileSetEditor::SHAPE_KEEP_INSIDE_TILE]->set_pressed(state["keep_inside_tile"]);
3617 }
3618
3619 if (state.has("show_information")) {
3620 tileset_editor->tools[TileSetEditor::VISIBLE_INFO]->set_pressed(state["show_information"]);
3621 }
3622 }
3623
TileSetEditorPlugin(EditorNode * p_node)3624 TileSetEditorPlugin::TileSetEditorPlugin(EditorNode *p_node) {
3625 editor = p_node;
3626 tileset_editor = memnew(TileSetEditor(p_node));
3627
3628 tileset_editor->set_custom_minimum_size(Size2(0, 200) * EDSCALE);
3629 tileset_editor->hide();
3630
3631 tileset_editor_button = p_node->add_bottom_panel_item(TTR("TileSet"), tileset_editor);
3632 tileset_editor_button->hide();
3633 }
3634