summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--editor/plugins/tile_set_editor_plugin.cpp503
-rw-r--r--editor/plugins/tile_set_editor_plugin.h28
2 files changed, 507 insertions, 24 deletions
diff --git a/editor/plugins/tile_set_editor_plugin.cpp b/editor/plugins/tile_set_editor_plugin.cpp
index 5b34aaa92a..75417d986c 100644
--- a/editor/plugins/tile_set_editor_plugin.cpp
+++ b/editor/plugins/tile_set_editor_plugin.cpp
@@ -201,6 +201,7 @@ void TileSetEditor::_bind_methods() {
ClassDB::bind_method("_zoom_out", &TileSetEditor::_zoom_out);
ClassDB::bind_method("_zoom_reset", &TileSetEditor::_zoom_reset);
ClassDB::bind_method("_select_edited_shape_coord", &TileSetEditor::_select_edited_shape_coord);
+ ClassDB::bind_method("_sort_tiles", &TileSetEditor::_sort_tiles);
ClassDB::bind_method("edit", &TileSetEditor::edit);
ClassDB::bind_method("add_texture", &TileSetEditor::add_texture);
@@ -234,6 +235,8 @@ void TileSetEditor::_notification(int p_what) {
tools[BITMASK_CLEAR]->set_icon(get_icon("Clear", "EditorIcons"));
tools[SHAPE_NEW_POLYGON]->set_icon(get_icon("CollisionPolygon2D", "EditorIcons"));
tools[SHAPE_NEW_RECTANGLE]->set_icon(get_icon("CollisionShape2D", "EditorIcons"));
+ tools[SELECT_PREVIOUS]->set_icon(get_icon("ArrowLeft", "EditorIcons"));
+ tools[SELECT_NEXT]->set_icon(get_icon("ArrowRight", "EditorIcons"));
tools[SHAPE_DELETE]->set_icon(get_icon("Remove", "EditorIcons"));
tools[SHAPE_KEEP_INSIDE_TILE]->set_icon(get_icon("Snap", "EditorIcons"));
tools[TOOL_GRID_SNAP]->set_icon(get_icon("SnapGrid", "EditorIcons"));
@@ -241,6 +244,7 @@ void TileSetEditor::_notification(int p_what) {
tools[ZOOM_1]->set_icon(get_icon("ZoomReset", "EditorIcons"));
tools[ZOOM_IN]->set_icon(get_icon("ZoomMore", "EditorIcons"));
tools[VISIBLE_INFO]->set_icon(get_icon("InformationSign", "EditorIcons"));
+ _update_toggle_shape_button();
tool_editmode[EDITMODE_REGION]->set_icon(get_icon("RegionEdit", "EditorIcons"));
tool_editmode[EDITMODE_COLLISION]->set_icon(get_icon("StaticBody2D", "EditorIcons"));
@@ -324,11 +328,29 @@ TileSetEditor::TileSetEditor(EditorNode *p_editor) {
tool_workspacemode[i]->connect("pressed", this, "_on_workspace_mode_changed", varray(i));
tool_hb->add_child(tool_workspacemode[i]);
}
+
Control *spacer = memnew(Control);
spacer->set_h_size_flags(Control::SIZE_EXPAND_FILL);
tool_hb->add_child(spacer);
tool_hb->move_child(spacer, WORKSPACE_CREATE_SINGLE);
+ tools[SELECT_NEXT] = memnew(ToolButton);
+ tool_hb->add_child(tools[SELECT_NEXT]);
+ tool_hb->move_child(tools[SELECT_NEXT], WORKSPACE_CREATE_SINGLE);
+ tools[SELECT_NEXT]->set_shortcut(ED_SHORTCUT("tileset_editor/next_shape", TTR("Select next coordinate"), KEY_PAGEDOWN));
+ tools[SELECT_NEXT]->connect("pressed", this, "_on_tool_clicked", varray(SELECT_NEXT));
+ tools[SELECT_NEXT]->set_tooltip(TTR("Select the next shape, subtile, or Tile."));
+ tools[SELECT_PREVIOUS] = memnew(ToolButton);
+ tool_hb->add_child(tools[SELECT_PREVIOUS]);
+ tool_hb->move_child(tools[SELECT_PREVIOUS], WORKSPACE_CREATE_SINGLE);
+ tools[SELECT_PREVIOUS]->set_shortcut(ED_SHORTCUT("tileset_editor/previous_shape", TTR("Select previous coordinate"), KEY_PAGEUP));
+ tools[SELECT_PREVIOUS]->set_tooltip(TTR("Select the previous shape, subtile, or Tile."));
+ tools[SELECT_PREVIOUS]->connect("pressed", this, "_on_tool_clicked", varray(SELECT_PREVIOUS));
+
+ VSeparator *separator_shape_selection = memnew(VSeparator);
+ tool_hb->add_child(separator_shape_selection);
+ tool_hb->move_child(separator_shape_selection, WORKSPACE_CREATE_SINGLE);
+
tool_workspacemode[WORKSPACE_EDIT]->set_pressed(true);
workspace_mode = WORKSPACE_EDIT;
@@ -391,6 +413,12 @@ TileSetEditor::TileSetEditor(EditorNode *p_editor) {
tools[SHAPE_NEW_POLYGON]->set_button_group(tg);
tools[SHAPE_NEW_POLYGON]->set_tooltip(TTR("Create a new polygon."));
+ separator_shape_toggle = memnew(VSeparator);
+ toolbar->add_child(separator_shape_toggle);
+ tools[SHAPE_TOGGLE_TYPE] = memnew(ToolButton);
+ tools[SHAPE_TOGGLE_TYPE]->connect("pressed", this, "_on_tool_clicked", varray(SHAPE_TOGGLE_TYPE));
+ toolbar->add_child(tools[SHAPE_TOGGLE_TYPE]);
+
separator_delete = memnew(VSeparator);
toolbar->add_child(separator_delete);
tools[SHAPE_DELETE] = memnew(ToolButton);
@@ -744,6 +772,7 @@ void TileSetEditor::_on_edit_mode_changed(int p_edit_mode) {
} break;
default: {}
}
+ _update_toggle_shape_button();
workspace->update();
}
@@ -1375,8 +1404,7 @@ void TileSetEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) {
}
undo_redo->create_action(TTR("Edit Collision Polygon"));
- undo_redo->add_do_method(edited_collision_shape.ptr(), "set_points", points);
- undo_redo->add_undo_method(edited_collision_shape.ptr(), "set_points", edited_collision_shape->get_points());
+ _set_edited_shape_points(points);
undo_redo->add_do_method(this, "_select_edited_shape_coord");
undo_redo->add_undo_method(this, "_select_edited_shape_coord");
undo_redo->commit_action();
@@ -1454,7 +1482,7 @@ void TileSetEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) {
workspace->update();
} else {
creating_shape = true;
- edited_collision_shape = Ref<ConvexPolygonShape2D>();
+ _set_edited_collision_shape(Ref<ConvexPolygonShape2D>());
current_shape.resize(0);
current_shape.push_back(snap_point(pos));
workspace->update();
@@ -1474,7 +1502,7 @@ void TileSetEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) {
} else if (tools[SHAPE_NEW_RECTANGLE]->is_pressed()) {
if (mb.is_valid()) {
if (mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) {
- edited_collision_shape = Ref<ConvexPolygonShape2D>();
+ _set_edited_collision_shape(Ref<ConvexPolygonShape2D>());
current_shape.resize(0);
current_shape.push_back(snap_point(shape_anchor));
current_shape.push_back(snap_point(shape_anchor + Vector2(current_tile_region.size.x, 0)));
@@ -1519,6 +1547,49 @@ void TileSetEditor::_on_tool_clicked(int p_tool) {
undo_redo->add_do_method(workspace, "update");
undo_redo->add_undo_method(workspace, "update");
undo_redo->commit_action();
+ } else if (p_tool == SHAPE_TOGGLE_TYPE) {
+ if (edited_collision_shape.is_valid()) {
+ Ref<ConvexPolygonShape2D> convex = edited_collision_shape;
+ Ref<ConcavePolygonShape2D> concave = edited_collision_shape;
+ Ref<Shape2D> previous_shape = edited_collision_shape;
+ Array sd = tileset->call("tile_get_shapes", get_current_tile());
+
+ if (convex.is_valid()) {
+ // Make concave
+ undo_redo->create_action(TTR("Make Polygon Concave"));
+ Ref<ConcavePolygonShape2D> _concave = memnew(ConcavePolygonShape2D);
+ edited_collision_shape = _concave;
+ _set_edited_shape_points(_get_collision_shape_points(convex));
+ } else if (concave.is_valid()) {
+ // Make convex
+ undo_redo->create_action(TTR("Make Polygon Convex"));
+ Ref<ConvexPolygonShape2D> _convex = memnew(ConvexPolygonShape2D);
+ edited_collision_shape = _convex;
+ _set_edited_shape_points(_get_collision_shape_points(concave));
+ } else {
+ // Shoudn't haphen
+ }
+ for (int i = 0; i < sd.size(); i++) {
+ if (sd[i].get("shape") == previous_shape) {
+ undo_redo->add_undo_method(tileset.ptr(), "tile_set_shapes", get_current_tile(), sd.duplicate());
+ sd.remove(i);
+ sd.insert(i, edited_collision_shape);
+ undo_redo->add_do_method(tileset.ptr(), "tile_set_shapes", get_current_tile(), sd);
+ undo_redo->add_do_method(this, "_select_edited_shape_coord");
+ undo_redo->add_undo_method(this, "_select_edited_shape_coord");
+ undo_redo->commit_action();
+ break;
+ }
+ }
+ _update_toggle_shape_button();
+ workspace->update();
+ workspace_container->update();
+ helper->_change_notify("");
+ }
+ } else if (p_tool == SELECT_NEXT) {
+ _select_next_shape();
+ } else if (p_tool == SELECT_PREVIOUS) {
+ _select_previous_shape();
} else if (p_tool == SHAPE_DELETE) {
if (creating_shape) {
creating_shape = false;
@@ -1641,6 +1712,378 @@ void TileSetEditor::_on_grid_snap_toggled(bool p_val) {
workspace->update();
}
+Vector<Vector2> TileSetEditor::_get_collision_shape_points(const Ref<Shape2D> &p_shape) {
+ Ref<ConvexPolygonShape2D> convex = p_shape;
+ Ref<ConcavePolygonShape2D> concave = p_shape;
+ if (convex.is_valid()) {
+ return convex->get_points();
+ } else if (concave.is_valid()) {
+ Vector<Vector2> points;
+ for (int i = 0; i < concave->get_segments().size(); i += 2) {
+ points.push_back(concave->get_segments()[i]);
+ }
+ return points;
+ } else {
+ return Vector<Vector2>();
+ }
+}
+
+Vector<Vector2> TileSetEditor::_get_edited_shape_points() {
+ return _get_collision_shape_points(edited_collision_shape);
+}
+
+void TileSetEditor::_set_edited_shape_points(const Vector<Vector2> points) {
+ Ref<ConvexPolygonShape2D> convex = edited_collision_shape;
+ Ref<ConcavePolygonShape2D> concave = edited_collision_shape;
+ if (convex.is_valid()) {
+ undo_redo->add_do_method(convex.ptr(), "set_points", points);
+ undo_redo->add_undo_method(convex.ptr(), "set_points", _get_edited_shape_points());
+ } else if (concave.is_valid()) {
+ PoolVector2Array segments;
+ for (int i = 0; i < points.size() - 1; i++) {
+ segments.push_back(points[i]);
+ segments.push_back(points[i + 1]);
+ }
+ segments.push_back(points[points.size() - 1]);
+ segments.push_back(points[0]);
+ concave->set_segments(segments);
+ undo_redo->add_do_method(concave.ptr(), "set_segments", segments);
+ undo_redo->add_undo_method(concave.ptr(), "set_segments", concave->get_segments());
+ } else {
+ // Invalid shape
+ }
+}
+
+void TileSetEditor::_update_tile_data() {
+ current_tile_data.clear();
+ if (get_current_tile() < 0)
+ return;
+
+ Vector<TileSet::ShapeData> sd = tileset->tile_get_shapes(get_current_tile());
+ if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE) {
+ SubtileData data;
+ for (int i = 0; i < sd.size(); i++) {
+ data.collisions.push_back(sd[i].shape);
+ }
+ data.navigation_shape = tileset->tile_get_navigation_polygon(get_current_tile());
+ data.occlusion_shape = tileset->tile_get_light_occluder(get_current_tile());
+ current_tile_data[Vector2i()] = data;
+ } else {
+ int spacing = tileset->autotile_get_spacing(get_current_tile());
+ Vector2 size = tileset->tile_get_region(get_current_tile()).size;
+ Vector2i cell_count = size / (tileset->autotile_get_size(get_current_tile()) + Vector2(spacing, spacing));
+ for (int y = 0; y < cell_count.y; y++) {
+ for (int x = 0; x < cell_count.x; x++) {
+ SubtileData data;
+ Vector2i coord(x, y);
+ for (int i = 0; i < sd.size(); i++) {
+ if (sd[i].autotile_coord == coord) {
+ data.collisions.push_back(sd[i].shape);
+ }
+ }
+ data.navigation_shape = tileset->autotile_get_navigation_polygon(get_current_tile(), coord);
+ data.occlusion_shape = tileset->tile_get_light_occluder(get_current_tile());
+ current_tile_data[coord] = data;
+ }
+ }
+ }
+}
+
+void TileSetEditor::_update_toggle_shape_button() {
+ Ref<ConvexPolygonShape2D> convex = edited_collision_shape;
+ Ref<ConcavePolygonShape2D> concave = edited_collision_shape;
+ separator_shape_toggle->show();
+ tools[SHAPE_TOGGLE_TYPE]->show();
+ if (edit_mode != EDITMODE_COLLISION || !edited_collision_shape.is_valid()) {
+ separator_shape_toggle->hide();
+ tools[SHAPE_TOGGLE_TYPE]->hide();
+ } else if (concave.is_valid()) {
+ tools[SHAPE_TOGGLE_TYPE]->set_icon(get_icon("ConvexPolygonShape2D", "EditorIcons"));
+ tools[SHAPE_TOGGLE_TYPE]->set_text("Make Convex");
+ } else if (convex.is_valid()) {
+ tools[SHAPE_TOGGLE_TYPE]->set_icon(get_icon("ConcavePolygonShape2D", "EditorIcons"));
+ tools[SHAPE_TOGGLE_TYPE]->set_text("Make Concave");
+ } else {
+ // Shoudn't happen
+ separator_shape_toggle->hide();
+ tools[SHAPE_TOGGLE_TYPE]->hide();
+ }
+}
+
+void TileSetEditor::_select_next_tile() {
+ Array tiles = _get_tiles_in_current_texture(true);
+ if (tiles.size() == 0) {
+ set_current_tile(-1);
+ } else if (get_current_tile() == -1) {
+ set_current_tile(tiles[0]);
+ } else {
+ int index = tiles.find(get_current_tile());
+ if (index < 0) {
+ set_current_tile(tiles[0]);
+ } else if (index == tiles.size() - 1) {
+ set_current_tile(tiles[0]);
+ } else {
+ set_current_tile(tiles[index + 1]);
+ }
+ }
+ if (get_current_tile() == -1) {
+ return;
+ } else if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE) {
+ return;
+ } else {
+ switch (edit_mode) {
+ case EDITMODE_COLLISION:
+ case EDITMODE_OCCLUSION:
+ case EDITMODE_NAVIGATION:
+ case EDITMODE_PRIORITY:
+ case EDITMODE_Z_INDEX: {
+ edited_shape_coord = Vector2();
+ _select_edited_shape_coord();
+ } break;
+ default: {}
+ }
+ }
+}
+
+void TileSetEditor::_select_previous_tile() {
+ Array tiles = _get_tiles_in_current_texture(true);
+ if (tiles.size() == 0) {
+ set_current_tile(-1);
+ } else if (get_current_tile() == -1) {
+ set_current_tile(tiles[tiles.size() - 1]);
+ } else {
+ int index = tiles.find(get_current_tile());
+ if (index <= 0) {
+ set_current_tile(tiles[tiles.size() - 1]);
+ } else {
+ set_current_tile(tiles[index - 1]);
+ }
+ }
+ if (get_current_tile() == -1) {
+ return;
+ } else if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE) {
+ return;
+ } else {
+ switch (edit_mode) {
+ case EDITMODE_COLLISION:
+ case EDITMODE_OCCLUSION:
+ case EDITMODE_NAVIGATION:
+ case EDITMODE_PRIORITY:
+ case EDITMODE_Z_INDEX: {
+ int spacing = tileset->autotile_get_spacing(get_current_tile());
+ Vector2 size = tileset->tile_get_region(get_current_tile()).size;
+ Vector2i cell_count = size / (tileset->autotile_get_size(get_current_tile()) + Vector2(spacing, spacing));
+ cell_count -= Vector2(1, 1);
+ edited_shape_coord = cell_count;
+ _select_edited_shape_coord();
+ } break;
+ default: {}
+ }
+ }
+}
+
+Array TileSetEditor::_get_tiles_in_current_texture(bool sorted) {
+ Array a;
+ List<int> all_tiles;
+ if (!get_current_texture().is_valid()) {
+ return a;
+ }
+ tileset->get_tile_list(&all_tiles);
+ for (int i = 0; i < all_tiles.size(); i++) {
+ if (tileset->tile_get_texture(all_tiles[i]) == get_current_texture()) {
+ a.push_back(all_tiles[i]);
+ }
+ }
+ if (sorted) {
+ a.sort_custom(this, "_sort_tiles");
+ }
+ return a;
+}
+
+bool TileSetEditor::_sort_tiles(Variant p_a, Variant p_b) {
+ int a = p_a;
+ int b = p_b;
+
+ Vector2 pos_a = tileset->tile_get_region(a).position;
+ Vector2 pos_b = tileset->tile_get_region(b).position;
+ if (pos_a.y < pos_b.y) {
+ return true;
+
+ } else if (pos_a.y == pos_b.y) {
+ if (pos_a.x < pos_b.x) {
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+}
+
+void TileSetEditor::_select_next_subtile() {
+ if (get_current_tile() == -1) {
+ _select_next_tile();
+ return;
+ }
+ if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE) {
+ _select_next_tile();
+ } else if (edit_mode == EDITMODE_REGION || edit_mode == EDITMODE_BITMASK || edit_mode == EDITMODE_ICON) {
+ _select_next_tile();
+ } else {
+ int spacing = tileset->autotile_get_spacing(get_current_tile());
+ Vector2 size = tileset->tile_get_region(get_current_tile()).size;
+ Vector2i cell_count = size / (tileset->autotile_get_size(get_current_tile()) + Vector2(spacing, spacing));
+ if (edited_shape_coord.x >= cell_count.x - 1 && edited_shape_coord.y >= cell_count.y - 1) {
+ _select_next_tile();
+ } else {
+ edited_shape_coord.x++;
+ if (edited_shape_coord.x >= cell_count.x) {
+ edited_shape_coord.x = 0;
+ edited_shape_coord.y++;
+ }
+ _select_edited_shape_coord();
+ }
+ }
+}
+
+void TileSetEditor::_select_previous_subtile() {
+ if (get_current_tile() == -1) {
+ _select_previous_tile();
+ return;
+ }
+ if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE) {
+ _select_previous_tile();
+ } else if (edit_mode == EDITMODE_REGION || edit_mode == EDITMODE_BITMASK || edit_mode == EDITMODE_ICON) {
+ _select_previous_tile();
+ } else {
+ int spacing = tileset->autotile_get_spacing(get_current_tile());
+ Vector2 size = tileset->tile_get_region(get_current_tile()).size;
+ Vector2i cell_count = size / (tileset->autotile_get_size(get_current_tile()) + Vector2(spacing, spacing));
+ if (edited_shape_coord.x <= 0 && edited_shape_coord.y <= 0) {
+ _select_previous_tile();
+ } else {
+ edited_shape_coord.x--;
+ if (edited_shape_coord.x == -1) {
+ edited_shape_coord.x = cell_count.x - 1;
+ edited_shape_coord.y--;
+ }
+ _select_edited_shape_coord();
+ }
+ }
+}
+
+void TileSetEditor::_select_next_shape() {
+ if (get_current_tile() == -1) {
+ _select_next_subtile();
+ } else if (edit_mode != EDITMODE_COLLISION) {
+ _select_next_subtile();
+ } else {
+ Vector2i edited_coord = Vector2();
+ if (tileset->tile_get_tile_mode(get_current_tile()) != TileSet::SINGLE_TILE) {
+ edited_coord = edited_shape_coord;
+ }
+ SubtileData data = current_tile_data[edited_coord];
+ if (data.collisions.size() == 0) {
+ _select_next_subtile();
+ } else {
+ int index = data.collisions.find(edited_collision_shape);
+ if (index < 0) {
+ _set_edited_collision_shape(data.collisions[0]);
+ } else if (index == data.collisions.size() - 1) {
+ _select_next_subtile();
+ } else {
+ _set_edited_collision_shape(data.collisions[index + 1]);
+ }
+ }
+ current_shape.resize(0);
+ Rect2 current_tile_region = tileset->tile_get_region(get_current_tile());
+ current_tile_region.position += WORKSPACE_MARGIN;
+
+ int spacing = tileset->autotile_get_spacing(get_current_tile());
+ Vector2 size = tileset->autotile_get_size(get_current_tile());
+ Vector2 shape_anchor = edited_shape_coord;
+ shape_anchor.x *= (size.x + spacing);
+ shape_anchor.y *= (size.y + spacing);
+ current_tile_region.position += shape_anchor;
+
+ if (edited_collision_shape.is_valid()) {
+ for (int i = 0; i < _get_edited_shape_points().size(); i++) {
+ current_shape.push_back(_get_edited_shape_points()[i] + current_tile_region.position);
+ }
+ }
+ workspace->update();
+ workspace_container->update();
+ helper->_change_notify("");
+ }
+}
+
+void TileSetEditor::_select_previous_shape() {
+ if (get_current_tile() == -1) {
+ _select_previous_subtile();
+ if (get_current_tile() != -1 && edit_mode == EDITMODE_COLLISION) {
+ SubtileData data = current_tile_data[Vector2i(edited_shape_coord)];
+ if (data.collisions.size() > 1) {
+ _set_edited_collision_shape(data.collisions[data.collisions.size() - 1]);
+ }
+ } else {
+ return;
+ }
+ } else if (edit_mode != EDITMODE_COLLISION) {
+ _select_previous_subtile();
+ } else {
+ Vector2i edited_coord = Vector2();
+ if (tileset->tile_get_tile_mode(get_current_tile()) != TileSet::SINGLE_TILE) {
+ edited_coord = edited_shape_coord;
+ }
+ SubtileData data = current_tile_data[edited_coord];
+ if (data.collisions.size() == 0) {
+ _select_previous_subtile();
+ data = current_tile_data[Vector2i(edited_shape_coord)];
+ if (data.collisions.size() > 1) {
+ _set_edited_collision_shape(data.collisions[data.collisions.size() - 1]);
+ }
+ } else {
+ int index = data.collisions.find(edited_collision_shape);
+ if (index < 0) {
+ _set_edited_collision_shape(data.collisions[data.collisions.size() - 1]);
+ } else if (index == 0) {
+ _select_previous_subtile();
+ data = current_tile_data[Vector2i(edited_shape_coord)];
+ if (data.collisions.size() > 1) {
+ _set_edited_collision_shape(data.collisions[data.collisions.size() - 1]);
+ }
+ } else {
+ _set_edited_collision_shape(data.collisions[index - 1]);
+ }
+ }
+
+ current_shape.resize(0);
+ Rect2 current_tile_region = tileset->tile_get_region(get_current_tile());
+ current_tile_region.position += WORKSPACE_MARGIN;
+
+ int spacing = tileset->autotile_get_spacing(get_current_tile());
+ Vector2 size = tileset->autotile_get_size(get_current_tile());
+ Vector2 shape_anchor = edited_shape_coord;
+ shape_anchor.x *= (size.x + spacing);
+ shape_anchor.y *= (size.y + spacing);
+ current_tile_region.position += shape_anchor;
+
+ if (edited_collision_shape.is_valid()) {
+ for (int i = 0; i < _get_edited_shape_points().size(); i++) {
+ current_shape.push_back(_get_edited_shape_points()[i] + current_tile_region.position);
+ }
+ }
+ workspace->update();
+ workspace_container->update();
+ helper->_change_notify("");
+ }
+}
+
+void TileSetEditor::_set_edited_collision_shape(const Ref<Shape2D> &p_shape) {
+ edited_collision_shape = p_shape;
+ _update_toggle_shape_button();
+}
+
void TileSetEditor::_set_snap_step(Vector2 p_val) {
snap_step.x = CLAMP(p_val.x, 0, 256);
snap_step.y = CLAMP(p_val.y, 0, 256);
@@ -1937,16 +2380,29 @@ void TileSetEditor::draw_polygon_shapes() {
}
anchor += WORKSPACE_MARGIN;
anchor += tileset->tile_get_region(t_id).position;
- Ref<ConvexPolygonShape2D> shape = sd[i].shape;
+ Ref<Shape2D> shape = sd[i].shape;
if (shape.is_valid()) {
Color c_bg;
Color c_border;
+ Ref<ConvexPolygonShape2D> convex = shape;
+ bool is_convex = convex.is_valid();
if ((tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE || coord == edited_shape_coord) && sd[i].shape == edited_collision_shape) {
- c_bg = Color(0, 1, 1, 0.5);
- c_border = Color(0, 1, 1);
+ if (is_convex) {
+ c_bg = Color(0, 1, 1, 0.5);
+ c_border = Color(0, 1, 1);
+ } else {
+ c_bg = Color(0.8, 0, 1, 0.5);
+ c_border = Color(0.8, 0, 1);
+ }
} else {
- c_bg = Color(0.9, 0.7, 0.07, 0.5);
- c_border = Color(0.9, 0.7, 0.07, 1);
+ if (is_convex) {
+ c_bg = Color(0.9, 0.7, 0.07, 0.5);
+ c_border = Color(0.9, 0.7, 0.07, 1);
+
+ } else {
+ c_bg = Color(0.9, 0.45, 0.075, 0.5);
+ c_border = Color(0.9, 0.45, 0.075);
+ }
}
Vector<Vector2> polygon;
Vector<Color> colors;
@@ -1956,8 +2412,8 @@ void TileSetEditor::draw_polygon_shapes() {
colors.push_back(c_bg);
}
} else {
- for (int j = 0; j < shape->get_points().size(); j++) {
- polygon.push_back(shape->get_points()[j] + anchor);
+ for (int j = 0; j < _get_collision_shape_points(shape).size(); j++) {
+ polygon.push_back(_get_collision_shape_points(shape)[j] + anchor);
colors.push_back(c_bg);
}
}
@@ -2174,11 +2630,11 @@ void TileSetEditor::close_shape(const Vector2 &shape_anchor) {
if (current_shape.size() >= 3) {
Ref<ConvexPolygonShape2D> shape = memnew(ConvexPolygonShape2D);
- Vector<Vector2> segments;
+ Vector<Vector2> points;
float p_total = 0;
for (int i = 0; i < current_shape.size(); i++) {
- segments.push_back(current_shape[i] - shape_anchor);
+ points.push_back(current_shape[i] - shape_anchor);
if (i != current_shape.size() - 1)
p_total += ((current_shape[i + 1].x - current_shape[i].x) * (-current_shape[i + 1].y + (-current_shape[i].y)));
@@ -2187,9 +2643,9 @@ void TileSetEditor::close_shape(const Vector2 &shape_anchor) {
}
if (p_total < 0)
- segments.invert();
+ points.invert();
- shape->set_points(segments);
+ shape->set_points(points);
undo_redo->create_action(TTR("Create Collision Polygon"));
// Necessary to get the version that returns a Array instead of a Vector.
@@ -2274,6 +2730,7 @@ void TileSetEditor::close_shape(const Vector2 &shape_anchor) {
}
void TileSetEditor::select_coord(const Vector2 &coord) {
+ _update_tile_data();
current_shape = PoolVector2Array();
if (get_current_tile() == -1)
return;
@@ -2281,7 +2738,7 @@ void TileSetEditor::select_coord(const Vector2 &coord) {
current_tile_region.position += WORKSPACE_MARGIN;
if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE) {
if (edited_collision_shape != tileset->tile_get_shape(get_current_tile(), 0))
- edited_collision_shape = tileset->tile_get_shape(get_current_tile(), 0);
+ _set_edited_collision_shape(tileset->tile_get_shape(get_current_tile(), 0));
if (edited_occlusion_shape != tileset->tile_get_light_occluder(get_current_tile()))
edited_occlusion_shape = tileset->tile_get_light_occluder(get_current_tile());
if (edited_navigation_shape != tileset->tile_get_navigation_polygon(get_current_tile()))
@@ -2290,8 +2747,8 @@ void TileSetEditor::select_coord(const Vector2 &coord) {
if (edit_mode == EDITMODE_COLLISION) {
current_shape.resize(0);
if (edited_collision_shape.is_valid()) {
- for (int i = 0; i < edited_collision_shape->get_points().size(); i++) {
- current_shape.push_back(edited_collision_shape->get_points()[i] + current_tile_region.position);
+ for (int i = 0; i < _get_edited_shape_points().size(); i++) {
+ current_shape.push_back(_get_edited_shape_points()[i] + current_tile_region.position);
}
}
} else if (edit_mode == EDITMODE_OCCLUSION) {
@@ -2318,13 +2775,13 @@ void TileSetEditor::select_coord(const Vector2 &coord) {
for (int i = 0; i < sd.size(); i++) {
if (sd[i].autotile_coord == coord) {
if (edited_collision_shape != sd[i].shape)
- edited_collision_shape = sd[i].shape;
+ _set_edited_collision_shape(sd[i].shape);
found_collision_shape = true;
break;
}
}
if (!found_collision_shape)
- edited_collision_shape = Ref<ConvexPolygonShape2D>(NULL);
+ _set_edited_collision_shape(Ref<ConvexPolygonShape2D>(NULL));
if (edited_occlusion_shape != tileset->autotile_get_light_occluder(get_current_tile(), coord))
edited_occlusion_shape = tileset->autotile_get_light_occluder(get_current_tile(), coord);
if (edited_navigation_shape != tileset->autotile_get_navigation_polygon(get_current_tile(), coord))
@@ -2339,8 +2796,8 @@ void TileSetEditor::select_coord(const Vector2 &coord) {
if (edit_mode == EDITMODE_COLLISION) {
current_shape.resize(0);
if (edited_collision_shape.is_valid()) {
- for (int j = 0; j < edited_collision_shape->get_points().size(); j++) {
- current_shape.push_back(edited_collision_shape->get_points()[j] + shape_anchor);
+ for (int j = 0; j < _get_edited_shape_points().size(); j++) {
+ current_shape.push_back(_get_edited_shape_points()[j] + shape_anchor);
}
}
} else if (edit_mode == EDITMODE_OCCLUSION) {
@@ -2495,7 +2952,7 @@ void TileSetEditor::update_workspace_tile_mode() {
for (int i = 0; i < EDITMODE_MAX; i++) {
tool_editmode[i]->hide();
}
- for (int i = 0; i < ZOOM_OUT; i++) {
+ for (int i = TOOL_SELECT; i < ZOOM_OUT; i++) {
tools[i]->hide();
}
diff --git a/editor/plugins/tile_set_editor_plugin.h b/editor/plugins/tile_set_editor_plugin.h
index 9c4aa80dcb..2827964592 100644
--- a/editor/plugins/tile_set_editor_plugin.h
+++ b/editor/plugins/tile_set_editor_plugin.h
@@ -34,6 +34,7 @@
#include "editor/editor_name_dialog.h"
#include "editor/editor_node.h"
#include "scene/2d/sprite.h"
+#include "scene/resources/concave_polygon_shape_2d.h"
#include "scene/resources/convex_polygon_shape_2d.h"
#include "scene/resources/tile_set.h"
@@ -76,12 +77,15 @@ class TileSetEditor : public HSplitContainer {
};
enum TileSetTools {
+ SELECT_PREVIOUS,
+ SELECT_NEXT,
TOOL_SELECT,
BITMASK_COPY,
BITMASK_PASTE,
BITMASK_CLEAR,
SHAPE_NEW_POLYGON,
SHAPE_NEW_RECTANGLE,
+ SHAPE_TOGGLE_TYPE,
SHAPE_DELETE,
SHAPE_KEEP_INSIDE_TILE,
TOOL_GRID_SNAP,
@@ -92,6 +96,12 @@ class TileSetEditor : public HSplitContainer {
TOOL_MAX
};
+ struct SubtileData {
+ Array collisions;
+ Ref<OccluderPolygon2D> occlusion_shape;
+ Ref<NavigationPolygon> navigation_shape;
+ };
+
Ref<TileSet> tileset;
TilesetEditorContext *helper;
EditorNode *editor;
@@ -115,13 +125,14 @@ class TileSetEditor : public HSplitContainer {
bool draw_edited_region;
Vector2 edited_shape_coord;
PoolVector2Array current_shape;
+ Map<Vector2i, SubtileData> current_tile_data;
Map<Vector2, uint16_t> bitmask_map_copy;
Vector2 snap_step;
Vector2 snap_offset;
Vector2 snap_separation;
- Ref<ConvexPolygonShape2D> edited_collision_shape;
+ Ref<Shape2D> edited_collision_shape;
Ref<OccluderPolygon2D> edited_occlusion_shape;
Ref<NavigationPolygon> edited_navigation_shape;
@@ -137,6 +148,7 @@ class TileSetEditor : public HSplitContainer {
HSeparator *separator_editmode;
HBoxContainer *toolbar;
ToolButton *tools[TOOL_MAX];
+ VSeparator *separator_shape_toggle;
VSeparator *separator_bitmask;
VSeparator *separator_delete;
VSeparator *separator_grid;
@@ -188,6 +200,20 @@ private:
void _on_priority_changed(float val);
void _on_z_index_changed(float val);
void _on_grid_snap_toggled(bool p_val);
+ Vector<Vector2> _get_collision_shape_points(const Ref<Shape2D> &p_shape);
+ Vector<Vector2> _get_edited_shape_points();
+ void _set_edited_shape_points(const Vector<Vector2> points);
+ void _update_tile_data();
+ void _update_toggle_shape_button();
+ void _select_next_tile();
+ void _select_previous_tile();
+ Array _get_tiles_in_current_texture(bool sorted = false);
+ bool _sort_tiles(Variant p_a, Variant p_b);
+ void _select_next_subtile();
+ void _select_previous_subtile();
+ void _select_next_shape();
+ void _select_previous_shape();
+ void _set_edited_collision_shape(const Ref<Shape2D> &p_shape);
void _set_snap_step(Vector2 p_val);
void _set_snap_off(Vector2 p_val);
void _set_snap_sep(Vector2 p_val);