From 30a615dd94f2dc990466f3953ad26a0e3f79a170 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gilles=20Roudi=C3=A8re?= Date: Wed, 9 Jun 2021 20:01:08 +0200 Subject: Implement painting properties over TileSets --- core/math/geometry_2d.h | 13 + doc/classes/TileData.xml | 40 +- doc/classes/TileSet.xml | 2 - editor/editor_properties.cpp | 107 +- editor/editor_properties.h | 2 + editor/icons/CenterView.svg | 1 + editor/icons/RectangleAddRemove.svg | 1 - editor/icons/TileChecked.svg | 1 + editor/icons/TileUnchecked.svg | 1 + editor/plugins/tiles/tile_atlas_view.cpp | 199 +- editor/plugins/tiles/tile_atlas_view.h | 19 +- editor/plugins/tiles/tile_data_editors.cpp | 2492 ++++++++- editor/plugins/tiles/tile_data_editors.h | 359 +- editor/plugins/tiles/tile_map_editor.cpp | 51 +- .../plugins/tiles/tile_set_atlas_source_editor.cpp | 1209 +++-- .../plugins/tiles/tile_set_atlas_source_editor.h | 30 +- editor/plugins/tiles/tile_set_editor.cpp | 96 +- editor/plugins/tiles/tile_set_editor.h | 12 - scene/resources/tile_set.cpp | 5677 ++++++++++---------- scene/resources/tile_set.h | 86 +- 20 files changed, 6644 insertions(+), 3754 deletions(-) create mode 100644 editor/icons/CenterView.svg delete mode 100644 editor/icons/RectangleAddRemove.svg create mode 100644 editor/icons/TileChecked.svg create mode 100644 editor/icons/TileUnchecked.svg diff --git a/core/math/geometry_2d.h b/core/math/geometry_2d.h index 4958b5ac6a..a2894bc1d3 100644 --- a/core/math/geometry_2d.h +++ b/core/math/geometry_2d.h @@ -362,6 +362,19 @@ public: return (intersections & 1); } + static bool is_segment_intersecting_polygon(const Vector2 &p_from, const Vector2 &p_to, const Vector &p_polygon) { + int c = p_polygon.size(); + const Vector2 *p = p_polygon.ptr(); + for (int i = 0; i < c; i++) { + const Vector2 &v1 = p[i]; + const Vector2 &v2 = p[(i + 1) % c]; + if (segment_intersects_segment(p_from, p_to, v1, v2, nullptr)) { + return true; + } + } + return false; + } + static real_t vec2_cross(const Point2 &O, const Point2 &A, const Point2 &B) { return (real_t)(A.x - O.x) * (B.y - O.y) - (real_t)(A.y - O.y) * (B.x - O.x); } diff --git a/doc/classes/TileData.xml b/doc/classes/TileData.xml index 1f0f807a08..bb793024eb 100644 --- a/doc/classes/TileData.xml +++ b/doc/classes/TileData.xml @@ -7,7 +7,7 @@ - + @@ -15,27 +15,27 @@ - + - + - - + + - + - + @@ -83,68 +83,68 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/doc/classes/TileSet.xml b/doc/classes/TileSet.xml index 7d8b589f78..55f232c004 100644 --- a/doc/classes/TileSet.xml +++ b/doc/classes/TileSet.xml @@ -289,8 +289,6 @@ - - diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index c28a61fecd..5ca596417b 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -491,6 +491,7 @@ void EditorPropertyEnum::update_property() { } void EditorPropertyEnum::setup(const Vector &p_options) { + options->clear(); int64_t current_val = 0; for (int i = 0; i < p_options.size(); i++) { Vector text_split = p_options[i].split(":"); @@ -2699,30 +2700,42 @@ void EditorInspectorDefaultPlugin::parse_begin(Object *p_object) { } bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Type p_type, const String &p_path, PropertyHint p_hint, const String &p_hint_text, int p_usage, bool p_wide) { + Control *editor = EditorInspectorDefaultPlugin::get_editor_for_property(p_object, p_type, p_path, p_hint, p_hint_text, p_usage, p_wide); + if (editor) { + add_property_editor(p_path, editor); + } + return false; +} + +void EditorInspectorDefaultPlugin::parse_end() { + //do none +} + +EditorProperty *EditorInspectorDefaultPlugin::get_editor_for_property(Object *p_object, Variant::Type p_type, const String &p_path, PropertyHint p_hint, const String &p_hint_text, int p_usage, bool p_wide) { double default_float_step = EDITOR_GET("interface/inspector/default_float_step"); switch (p_type) { // atomic types case Variant::NIL: { EditorPropertyNil *editor = memnew(EditorPropertyNil); - add_property_editor(p_path, editor); + return editor; } break; case Variant::BOOL: { EditorPropertyCheck *editor = memnew(EditorPropertyCheck); - add_property_editor(p_path, editor); + return editor; } break; case Variant::INT: { if (p_hint == PROPERTY_HINT_ENUM) { EditorPropertyEnum *editor = memnew(EditorPropertyEnum); Vector options = p_hint_text.split(","); editor->setup(options); - add_property_editor(p_path, editor); + return editor; } else if (p_hint == PROPERTY_HINT_FLAGS) { EditorPropertyFlags *editor = memnew(EditorPropertyFlags); Vector options = p_hint_text.split(","); editor->setup(options); - add_property_editor(p_path, editor); + return editor; } else if (p_hint == PROPERTY_HINT_LAYERS_2D_PHYSICS || p_hint == PROPERTY_HINT_LAYERS_2D_RENDER || @@ -2755,11 +2768,11 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ } EditorPropertyLayers *editor = memnew(EditorPropertyLayers); editor->setup(lt); - add_property_editor(p_path, editor); + return editor; } else if (p_hint == PROPERTY_HINT_OBJECT_ID) { EditorPropertyObjectID *editor = memnew(EditorPropertyObjectID); editor->setup(p_hint_text); - add_property_editor(p_path, editor); + return editor; } else { EditorPropertyInteger *editor = memnew(EditorPropertyInteger); @@ -2789,7 +2802,7 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ editor->setup(min, max, step, greater, lesser); - add_property_editor(p_path, editor); + return editor; } } break; case Variant::FLOAT: { @@ -2809,7 +2822,7 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ } editor->setup(full, flip); - add_property_editor(p_path, editor); + return editor; } else { EditorPropertyFloat *editor = memnew(EditorPropertyFloat); @@ -2841,7 +2854,7 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ editor->setup(min, max, step, hide_slider, exp_range, greater, lesser); - add_property_editor(p_path, editor); + return editor; } } break; case Variant::STRING: { @@ -2849,14 +2862,14 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ EditorPropertyTextEnum *editor = memnew(EditorPropertyTextEnum); Vector options = p_hint_text.split(","); editor->setup(options); - add_property_editor(p_path, editor); + return editor; } else if (p_hint == PROPERTY_HINT_MULTILINE_TEXT) { EditorPropertyMultilineText *editor = memnew(EditorPropertyMultilineText); - add_property_editor(p_path, editor); + return editor; } else if (p_hint == PROPERTY_HINT_TYPE_STRING) { EditorPropertyClassName *editor = memnew(EditorPropertyClassName); editor->setup("Object", p_hint_text); - add_property_editor(p_path, editor); + return editor; } else if (p_hint == PROPERTY_HINT_DIR || p_hint == PROPERTY_HINT_FILE || p_hint == PROPERTY_HINT_SAVE_FILE || p_hint == PROPERTY_HINT_GLOBAL_DIR || p_hint == PROPERTY_HINT_GLOBAL_FILE) { Vector extensions = p_hint_text.split(","); bool global = p_hint == PROPERTY_HINT_GLOBAL_DIR || p_hint == PROPERTY_HINT_GLOBAL_FILE; @@ -2867,7 +2880,7 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ if (save) { editor->set_save_mode(); } - add_property_editor(p_path, editor); + return editor; } else if (p_hint == PROPERTY_HINT_METHOD_OF_VARIANT_TYPE || p_hint == PROPERTY_HINT_METHOD_OF_BASE_TYPE || p_hint == PROPERTY_HINT_METHOD_OF_INSTANCE || @@ -2905,14 +2918,14 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ } } editor->setup(type, p_hint_text); - add_property_editor(p_path, editor); + return editor; } else { EditorPropertyText *editor = memnew(EditorPropertyText); if (p_hint == PROPERTY_HINT_PLACEHOLDER_TEXT) { editor->set_placeholder(p_hint_text); } - add_property_editor(p_path, editor); + return editor; } } break; @@ -2933,7 +2946,7 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ } editor->setup(min, max, step, hide_slider); - add_property_editor(p_path, editor); + return editor; } break; case Variant::VECTOR2I: { @@ -2948,7 +2961,7 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ } editor->setup(min, max, hide_slider); - add_property_editor(p_path, editor); + return editor; } break; case Variant::RECT2: { @@ -2966,7 +2979,7 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ } editor->setup(min, max, step, hide_slider); - add_property_editor(p_path, editor); + return editor; } break; case Variant::RECT2I: { EditorPropertyRect2i *editor = memnew(EditorPropertyRect2i(p_wide)); @@ -2980,7 +2993,7 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ } editor->setup(min, max, hide_slider); - add_property_editor(p_path, editor); + return editor; } break; case Variant::VECTOR3: { EditorPropertyVector3 *editor = memnew(EditorPropertyVector3(p_wide)); @@ -2997,7 +3010,7 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ } editor->setup(min, max, step, hide_slider); - add_property_editor(p_path, editor); + return editor; } break; case Variant::VECTOR3I: { @@ -3013,7 +3026,7 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ } editor->setup(min, max, hide_slider); - add_property_editor(p_path, editor); + return editor; } break; case Variant::TRANSFORM2D: { @@ -3031,7 +3044,7 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ } editor->setup(min, max, step, hide_slider); - add_property_editor(p_path, editor); + return editor; } break; case Variant::PLANE: { @@ -3049,7 +3062,7 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ } editor->setup(min, max, step, hide_slider); - add_property_editor(p_path, editor); + return editor; } break; case Variant::QUATERNION: { EditorPropertyQuaternion *editor = memnew(EditorPropertyQuaternion); @@ -3066,7 +3079,7 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ } editor->setup(min, max, step, hide_slider); - add_property_editor(p_path, editor); + return editor; } break; case Variant::AABB: { EditorPropertyAABB *editor = memnew(EditorPropertyAABB); @@ -3083,7 +3096,7 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ } editor->setup(min, max, step, hide_slider); - add_property_editor(p_path, editor); + return editor; } break; case Variant::BASIS: { EditorPropertyBasis *editor = memnew(EditorPropertyBasis); @@ -3100,7 +3113,7 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ } editor->setup(min, max, step, hide_slider); - add_property_editor(p_path, editor); + return editor; } break; case Variant::TRANSFORM3D: { EditorPropertyTransform3D *editor = memnew(EditorPropertyTransform3D); @@ -3117,7 +3130,7 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ } editor->setup(min, max, step, hide_slider); - add_property_editor(p_path, editor); + return editor; } break; @@ -3125,21 +3138,21 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ case Variant::COLOR: { EditorPropertyColor *editor = memnew(EditorPropertyColor); editor->setup(p_hint != PROPERTY_HINT_COLOR_NO_ALPHA); - add_property_editor(p_path, editor); + return editor; } break; case Variant::STRING_NAME: { if (p_hint == PROPERTY_HINT_ENUM) { EditorPropertyTextEnum *editor = memnew(EditorPropertyTextEnum); Vector options = p_hint_text.split(","); editor->setup(options, true); - add_property_editor(p_path, editor); + return editor; } else { EditorPropertyText *editor = memnew(EditorPropertyText); if (p_hint == PROPERTY_HINT_PLACEHOLDER_TEXT) { editor->set_placeholder(p_hint_text); } editor->set_string_name(true); - add_property_editor(p_path, editor); + return editor; } } break; case Variant::NODE_PATH: { @@ -3152,12 +3165,12 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ Vector sn = Variant(types); //convert via variant editor->setup(NodePath(), sn, (p_usage & PROPERTY_USAGE_NODE_PATH_FROM_SCENE_ROOT)); } - add_property_editor(p_path, editor); + return editor; } break; case Variant::RID: { EditorPropertyRID *editor = memnew(EditorPropertyRID); - add_property_editor(p_path, editor); + return editor; } break; case Variant::OBJECT: { EditorPropertyResource *editor = memnew(EditorPropertyResource); @@ -3176,70 +3189,66 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ } } - add_property_editor(p_path, editor); + return editor; } break; case Variant::DICTIONARY: { EditorPropertyDictionary *editor = memnew(EditorPropertyDictionary); - add_property_editor(p_path, editor); + return editor; } break; case Variant::ARRAY: { EditorPropertyArray *editor = memnew(EditorPropertyArray); editor->setup(Variant::ARRAY, p_hint_text); - add_property_editor(p_path, editor); + return editor; } break; case Variant::PACKED_BYTE_ARRAY: { EditorPropertyArray *editor = memnew(EditorPropertyArray); editor->setup(Variant::PACKED_BYTE_ARRAY); - add_property_editor(p_path, editor); + return editor; } break; case Variant::PACKED_INT32_ARRAY: { EditorPropertyArray *editor = memnew(EditorPropertyArray); editor->setup(Variant::PACKED_INT32_ARRAY); - add_property_editor(p_path, editor); + return editor; } break; case Variant::PACKED_INT64_ARRAY: { EditorPropertyArray *editor = memnew(EditorPropertyArray); editor->setup(Variant::PACKED_INT64_ARRAY); - add_property_editor(p_path, editor); + return editor; } break; case Variant::PACKED_FLOAT32_ARRAY: { EditorPropertyArray *editor = memnew(EditorPropertyArray); editor->setup(Variant::PACKED_FLOAT32_ARRAY); - add_property_editor(p_path, editor); + return editor; } break; case Variant::PACKED_FLOAT64_ARRAY: { EditorPropertyArray *editor = memnew(EditorPropertyArray); editor->setup(Variant::PACKED_FLOAT64_ARRAY); - add_property_editor(p_path, editor); + return editor; } break; case Variant::PACKED_STRING_ARRAY: { EditorPropertyArray *editor = memnew(EditorPropertyArray); editor->setup(Variant::PACKED_STRING_ARRAY); - add_property_editor(p_path, editor); + return editor; } break; case Variant::PACKED_VECTOR2_ARRAY: { EditorPropertyArray *editor = memnew(EditorPropertyArray); editor->setup(Variant::PACKED_VECTOR2_ARRAY); - add_property_editor(p_path, editor); + return editor; } break; case Variant::PACKED_VECTOR3_ARRAY: { EditorPropertyArray *editor = memnew(EditorPropertyArray); editor->setup(Variant::PACKED_VECTOR3_ARRAY); - add_property_editor(p_path, editor); + return editor; } break; case Variant::PACKED_COLOR_ARRAY: { EditorPropertyArray *editor = memnew(EditorPropertyArray); editor->setup(Variant::PACKED_COLOR_ARRAY); - add_property_editor(p_path, editor); + } break; default: { } } - return false; //can be overridden, although it will most likely be last anyway -} - -void EditorInspectorDefaultPlugin::parse_end() { - //do none + return nullptr; } diff --git a/editor/editor_properties.h b/editor/editor_properties.h index dcde7dda3d..522f4eebf6 100644 --- a/editor/editor_properties.h +++ b/editor/editor_properties.h @@ -649,6 +649,8 @@ public: virtual void parse_begin(Object *p_object) override; virtual bool parse_property(Object *p_object, Variant::Type p_type, const String &p_path, PropertyHint p_hint, const String &p_hint_text, int p_usage, bool p_wide = false) override; virtual void parse_end() override; + + static EditorProperty *get_editor_for_property(Object *p_object, Variant::Type p_type, const String &p_path, PropertyHint p_hint, const String &p_hint_text, int p_usage, bool p_wide = false); }; #endif // EDITOR_PROPERTIES_H diff --git a/editor/icons/CenterView.svg b/editor/icons/CenterView.svg new file mode 100644 index 0000000000..c2a918fe81 --- /dev/null +++ b/editor/icons/CenterView.svg @@ -0,0 +1 @@ + diff --git a/editor/icons/RectangleAddRemove.svg b/editor/icons/RectangleAddRemove.svg deleted file mode 100644 index 87e2155a0d..0000000000 --- a/editor/icons/RectangleAddRemove.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/editor/icons/TileChecked.svg b/editor/icons/TileChecked.svg new file mode 100644 index 0000000000..33b99a0e4c --- /dev/null +++ b/editor/icons/TileChecked.svg @@ -0,0 +1 @@ + diff --git a/editor/icons/TileUnchecked.svg b/editor/icons/TileUnchecked.svg new file mode 100644 index 0000000000..cd8db4ee19 --- /dev/null +++ b/editor/icons/TileUnchecked.svg @@ -0,0 +1 @@ + diff --git a/editor/plugins/tiles/tile_atlas_view.cpp b/editor/plugins/tiles/tile_atlas_view.cpp index 78f181e321..374a255df3 100644 --- a/editor/plugins/tiles/tile_atlas_view.cpp +++ b/editor/plugins/tiles/tile_atlas_view.cpp @@ -33,7 +33,6 @@ #include "core/input/input.h" #include "core/os/keyboard.h" #include "scene/gui/box_container.h" -#include "scene/gui/center_container.h" #include "scene/gui/label.h" #include "scene/gui/panel.h" #include "scene/gui/texture_rect.h" @@ -44,21 +43,41 @@ void TileAtlasView::_gui_input(const Ref &p_event) { bool ctrl = Input::get_singleton()->is_key_pressed(KEY_CTRL); - Ref b = p_event; - if (b.is_valid()) { - if (ctrl && b->is_pressed() && b->get_button_index() == MOUSE_BUTTON_WHEEL_DOWN) { + Ref mb = p_event; + if (mb.is_valid()) { + drag_type = DRAG_TYPE_NONE; + if (ctrl && mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_WHEEL_DOWN) { // Zoom out zoom_widget->set_zoom_by_increments(-2); - emit_signal("transform_changed", zoom_widget->get_zoom(), Vector2(scroll_container->get_h_scroll(), scroll_container->get_v_scroll())); - _update_zoom(zoom_widget->get_zoom(), true); + emit_signal("transform_changed", zoom_widget->get_zoom(), panning); + _update_zoom_and_panning(true); accept_event(); } - if (ctrl && b->is_pressed() && b->get_button_index() == MOUSE_BUTTON_WHEEL_UP) { + if (ctrl && mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_WHEEL_UP) { // Zoom in zoom_widget->set_zoom_by_increments(2); - emit_signal("transform_changed", zoom_widget->get_zoom(), Vector2(scroll_container->get_h_scroll(), scroll_container->get_v_scroll())); - _update_zoom(zoom_widget->get_zoom(), true); + emit_signal("transform_changed", zoom_widget->get_zoom(), panning); + _update_zoom_and_panning(true); + accept_event(); + } + + if (mb->get_button_index() == MOUSE_BUTTON_MIDDLE || mb->get_button_index() == MOUSE_BUTTON_RIGHT) { + if (mb->is_pressed()) { + drag_type = DRAG_TYPE_PAN; + } else { + drag_type = DRAG_TYPE_NONE; + } + accept_event(); + } + } + + Ref mm = p_event; + if (mm.is_valid()) { + if (drag_type == DRAG_TYPE_PAN) { + panning += mm->get_relative(); + _update_zoom_and_panning(); + emit_signal("transform_changed", zoom_widget->get_zoom(), panning); accept_event(); } } @@ -103,25 +122,27 @@ Size2i TileAtlasView::_compute_alternative_tiles_control_size() { return size; } -void TileAtlasView::_update_zoom(float p_zoom, bool p_zoom_on_mouse_pos, Vector2i p_scroll) { +void TileAtlasView::_update_zoom_and_panning(bool p_zoom_on_mouse_pos) { + float zoom = zoom_widget->get_zoom(); + // Compute the minimum sizes. Size2i base_tiles_control_size = _compute_base_tiles_control_size(); - base_tiles_root_control->set_custom_minimum_size(Vector2(base_tiles_control_size) * p_zoom); + base_tiles_root_control->set_custom_minimum_size(Vector2(base_tiles_control_size) * zoom); Size2i alternative_tiles_control_size = _compute_alternative_tiles_control_size(); - alternative_tiles_root_control->set_custom_minimum_size(Vector2(alternative_tiles_control_size) * p_zoom); + alternative_tiles_root_control->set_custom_minimum_size(Vector2(alternative_tiles_control_size) * zoom); // Set the texture for the base tiles. Ref texture = tile_set_atlas_source->get_texture(); // Set the scales. if (base_tiles_control_size.x > 0 && base_tiles_control_size.y > 0) { - base_tiles_drawing_root->set_scale(Vector2(p_zoom, p_zoom)); + base_tiles_drawing_root->set_scale(Vector2(zoom, zoom)); } else { base_tiles_drawing_root->set_scale(Vector2(1, 1)); } if (alternative_tiles_control_size.x > 0 && alternative_tiles_control_size.y > 0) { - alternative_tiles_drawing_root->set_scale(Vector2(p_zoom, p_zoom)); + alternative_tiles_drawing_root->set_scale(Vector2(zoom, zoom)); } else { alternative_tiles_drawing_root->set_scale(Vector2(1, 1)); } @@ -129,64 +150,40 @@ void TileAtlasView::_update_zoom(float p_zoom, bool p_zoom_on_mouse_pos, Vector2 // Update the margin container's margins. const char *constants[] = { "margin_left", "margin_top", "margin_right", "margin_bottom" }; for (int i = 0; i < 4; i++) { - margin_container->add_theme_constant_override(constants[i], margin_container_paddings[i] * p_zoom); + margin_container->add_theme_constant_override(constants[i], margin_container_paddings[i] * zoom); } // Update the backgrounds. background_left->update(); background_right->update(); - if (p_scroll != Vector2i(-1, -1)) { - scroll_container->set_h_scroll(p_scroll.x); - scroll_container->set_v_scroll(p_scroll.y); - } - // Zoom on the position. - if (previous_zoom != p_zoom) { - // TODO: solve this. - // There is however an issue with scrollcainter preventing this, as it seems - // that the scrollbars are not updated right aways after its children update. - - // Compute point on previous area. - /*Vector2 max = Vector2(scroll_container->get_h_scrollbar()->get_max(), scroll_container->get_v_scrollbar()->get_max()); - Vector2 min = Vector2(scroll_container->get_h_scrollbar()->get_min(), scroll_container->get_v_scrollbar()->get_min()); - Vector2 value = Vector2(scroll_container->get_h_scrollbar()->get_value(), scroll_container->get_v_scrollbar()->get_value()); - - Vector2 old_max = max * previous_zoom / p_zoom; - - Vector2 max_pixel_change = max - old_max; - Vector2 ratio = ((value + scroll_container->get_local_mouse_position()) / old_max).max(Vector2()).min(Vector2(1,1)); - Vector2 offset = max_pixel_change * ratio; - - print_line("--- ZOOMED ---"); - print_line(vformat("max: %s", max)); - print_line(vformat("min: %s", min)); - print_line(vformat("value: %s", value)); - print_line(vformat("size: %s", scroll_container->get_size())); - print_line(vformat("mouse_pos: %s", scroll_container->get_local_mouse_position())); - - print_line(vformat("ratio: %s", ratio)); - print_line(vformat("max_pixel_change: %s", max_pixel_change)); - print_line(vformat("offset: %s", offset)); - + if (p_zoom_on_mouse_pos) { + // Offset the panning relative to the center of panel. + Vector2 relative_mpos = get_local_mouse_position() - get_size() / 2; + panning = (panning - relative_mpos) * zoom / previous_zoom + relative_mpos; + } else { + // Center of panel. + panning = panning * zoom / previous_zoom; + } + button_center_view->set_disabled(panning.is_equal_approx(Vector2())); - print_line(vformat("value before: %s", Vector2(scroll_container->get_h_scroll(), scroll_container->get_v_scroll()))); - scroll_container->set_h_scroll(10000);//scroll_container->get_h_scroll()+offset.x); - scroll_container->set_v_scroll(10000);//scroll_container->get_v_scroll()+offset.y); - print_line(vformat("value after: %s", Vector2(scroll_container->get_h_scroll(), scroll_container->get_v_scroll()))); - */ + previous_zoom = zoom; - previous_zoom = p_zoom; - } + center_container->set_begin(panning - center_container->get_minimum_size() / 2); + center_container->set_size(center_container->get_minimum_size()); } -void TileAtlasView::_scroll_changed() { - emit_signal("transform_changed", zoom_widget->get_zoom(), Vector2(scroll_container->get_h_scroll(), scroll_container->get_v_scroll())); +void TileAtlasView::_zoom_widget_changed() { + _update_zoom_and_panning(); + emit_signal("transform_changed", zoom_widget->get_zoom(), panning); } -void TileAtlasView::_zoom_widget_changed() { - _update_zoom(zoom_widget->get_zoom()); - emit_signal("transform_changed", zoom_widget->get_zoom(), Vector2(scroll_container->get_h_scroll(), scroll_container->get_v_scroll())); +void TileAtlasView::_center_view() { + panning = Vector2(); + button_center_view->set_disabled(true); + _update_zoom_and_panning(); + emit_signal("transform_changed", zoom_widget->get_zoom(), panning); } void TileAtlasView::_base_tiles_root_control_gui_input(const Ref &p_event) { @@ -415,7 +412,7 @@ void TileAtlasView::set_atlas_source(TileSet *p_tile_set, TileSetAtlasSource *p_ _update_alternative_tiles_rect_cache(); // Update everything. - _update_zoom(zoom_widget->get_zoom()); + _update_zoom_and_panning(); // Change children control size. Size2i base_tiles_control_size = _compute_base_tiles_control_size(); @@ -448,9 +445,10 @@ float TileAtlasView::get_zoom() const { return zoom_widget->get_zoom(); }; -void TileAtlasView::set_transform(float p_zoom, Vector2i p_scroll) { +void TileAtlasView::set_transform(float p_zoom, Vector2i p_panning) { zoom_widget->set_zoom(p_zoom); - _update_zoom(zoom_widget->get_zoom(), false, p_scroll); + panning = p_panning; + _update_zoom_and_panning(); }; void TileAtlasView::set_padding(Side p_side, int p_padding) { @@ -525,7 +523,7 @@ Rect2i TileAtlasView::get_alternative_tile_rect(const Vector2i p_coords, int p_a } void TileAtlasView::update() { - scroll_container->update(); + base_tiles_draw->update(); base_tiles_texture_grid->update(); base_tiles_shape_grid->update(); base_tiles_dark->update(); @@ -534,124 +532,149 @@ void TileAtlasView::update() { background_right->update(); } +void TileAtlasView::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_READY: + button_center_view->set_icon(get_theme_icon("CenterView", "EditorIcons")); + break; + } +} + void TileAtlasView::_bind_methods() { + ClassDB::bind_method("_gui_input", &TileAtlasView::_gui_input); + ADD_SIGNAL(MethodInfo("transform_changed", PropertyInfo(Variant::FLOAT, "zoom"), PropertyInfo(Variant::VECTOR2, "scroll"))); } TileAtlasView::TileAtlasView() { - Panel *panel_container = memnew(Panel); - panel_container->set_h_size_flags(SIZE_EXPAND_FILL); - panel_container->set_v_size_flags(SIZE_EXPAND_FILL); - panel_container->set_anchors_and_offsets_preset(Control::PRESET_WIDE); - add_child(panel_container); - - //Scrolling - scroll_container = memnew(ScrollContainer); - scroll_container->get_h_scrollbar()->connect("value_changed", callable_mp(this, &TileAtlasView::_scroll_changed).unbind(1)); - scroll_container->get_v_scrollbar()->connect("value_changed", callable_mp(this, &TileAtlasView::_scroll_changed).unbind(1)); - panel_container->add_child(scroll_container); - scroll_container->set_anchors_and_offsets_preset(Control::PRESET_WIDE); + set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST); + + Panel *panel = memnew(Panel); + panel->set_clip_contents(true); + panel->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); + panel->set_anchors_and_offsets_preset(Control::PRESET_WIDE); + panel->set_h_size_flags(SIZE_EXPAND_FILL); + panel->set_v_size_flags(SIZE_EXPAND_FILL); + add_child(panel); + // Scrollingsc zoom_widget = memnew(EditorZoomWidget); add_child(zoom_widget); zoom_widget->set_anchors_and_offsets_preset(Control::PRESET_TOP_LEFT, Control::PRESET_MODE_MINSIZE, 2 * EDSCALE); zoom_widget->connect("zoom_changed", callable_mp(this, &TileAtlasView::_zoom_widget_changed).unbind(1)); - CenterContainer *center_container = memnew(CenterContainer); - center_container->set_h_size_flags(SIZE_EXPAND_FILL); - center_container->set_v_size_flags(SIZE_EXPAND_FILL); + button_center_view = memnew(Button); + button_center_view->set_icon(get_theme_icon("CenterView", "EditorIcons")); + button_center_view->set_anchors_and_offsets_preset(Control::PRESET_TOP_RIGHT, Control::PRESET_MODE_MINSIZE, 5); + button_center_view->connect("pressed", callable_mp(this, &TileAtlasView::_center_view)); + button_center_view->set_flat(true); + button_center_view->set_disabled(true); + add_child(button_center_view); + + center_container = memnew(CenterContainer); + center_container->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); + center_container->set_anchors_preset(Control::PRESET_CENTER); center_container->connect("gui_input", callable_mp(this, &TileAtlasView::_gui_input)); - scroll_container->add_child(center_container); + panel->add_child(center_container); missing_source_label = memnew(Label); missing_source_label->set_text(TTR("No atlas source with a valid texture selected.")); center_container->add_child(missing_source_label); margin_container = memnew(MarginContainer); + margin_container->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); center_container->add_child(margin_container); hbox = memnew(HBoxContainer); + hbox->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); hbox->add_theme_constant_override("separation", 10); hbox->hide(); margin_container->add_child(hbox); VBoxContainer *left_vbox = memnew(VBoxContainer); + left_vbox->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); hbox->add_child(left_vbox); VBoxContainer *right_vbox = memnew(VBoxContainer); + right_vbox->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); hbox->add_child(right_vbox); // Base tiles. Label *base_tile_label = memnew(Label); + base_tile_label->set_mouse_filter(Control::MOUSE_FILTER_PASS); base_tile_label->set_text(TTR("Base Tiles")); base_tile_label->set_align(Label::ALIGN_CENTER); left_vbox->add_child(base_tile_label); base_tiles_root_control = memnew(Control); + base_tiles_root_control->set_mouse_filter(Control::MOUSE_FILTER_PASS); base_tiles_root_control->set_v_size_flags(Control::SIZE_EXPAND_FILL); base_tiles_root_control->connect("gui_input", callable_mp(this, &TileAtlasView::_base_tiles_root_control_gui_input)); left_vbox->add_child(base_tiles_root_control); background_left = memnew(Control); + background_left->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); background_left->set_anchors_and_offsets_preset(Control::PRESET_WIDE); background_left->set_texture_repeat(TextureRepeat::TEXTURE_REPEAT_ENABLED); - background_left->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); background_left->connect("draw", callable_mp(this, &TileAtlasView::_draw_background_left)); base_tiles_root_control->add_child(background_left); base_tiles_drawing_root = memnew(Control); + base_tiles_drawing_root->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); base_tiles_drawing_root->set_anchors_and_offsets_preset(Control::PRESET_WIDE); base_tiles_drawing_root->set_texture_filter(TEXTURE_FILTER_NEAREST); - base_tiles_drawing_root->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); base_tiles_root_control->add_child(base_tiles_drawing_root); base_tiles_draw = memnew(Control); + base_tiles_draw->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); base_tiles_draw->set_anchors_and_offsets_preset(Control::PRESET_WIDE); base_tiles_draw->connect("draw", callable_mp(this, &TileAtlasView::_draw_base_tiles)); - base_tiles_draw->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); base_tiles_drawing_root->add_child(base_tiles_draw); base_tiles_texture_grid = memnew(Control); + base_tiles_texture_grid->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); base_tiles_texture_grid->set_anchors_and_offsets_preset(Control::PRESET_WIDE); base_tiles_texture_grid->connect("draw", callable_mp(this, &TileAtlasView::_draw_base_tiles_texture_grid)); - base_tiles_texture_grid->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); base_tiles_drawing_root->add_child(base_tiles_texture_grid); base_tiles_shape_grid = memnew(Control); + base_tiles_shape_grid->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); base_tiles_shape_grid->set_anchors_and_offsets_preset(Control::PRESET_WIDE); base_tiles_shape_grid->connect("draw", callable_mp(this, &TileAtlasView::_draw_base_tiles_shape_grid)); - base_tiles_shape_grid->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); base_tiles_drawing_root->add_child(base_tiles_shape_grid); base_tiles_dark = memnew(Control); + base_tiles_dark->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); base_tiles_dark->set_anchors_and_offsets_preset(Control::PRESET_WIDE); base_tiles_dark->connect("draw", callable_mp(this, &TileAtlasView::_draw_base_tiles_dark)); - base_tiles_dark->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); base_tiles_drawing_root->add_child(base_tiles_dark); // Alternative tiles. Label *alternative_tiles_label = memnew(Label); + alternative_tiles_label->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); alternative_tiles_label->set_text(TTR("Alternative Tiles")); alternative_tiles_label->set_align(Label::ALIGN_CENTER); right_vbox->add_child(alternative_tiles_label); alternative_tiles_root_control = memnew(Control); + alternative_tiles_root_control->set_mouse_filter(Control::MOUSE_FILTER_PASS); alternative_tiles_root_control->connect("gui_input", callable_mp(this, &TileAtlasView::_alternative_tiles_root_control_gui_input)); right_vbox->add_child(alternative_tiles_root_control); background_right = memnew(Control); + background_right->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); background_right->set_texture_repeat(TextureRepeat::TEXTURE_REPEAT_ENABLED); background_right->connect("draw", callable_mp(this, &TileAtlasView::_draw_background_right)); - background_right->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); + alternative_tiles_root_control->add_child(background_right); alternative_tiles_drawing_root = memnew(Control); - alternative_tiles_drawing_root->set_texture_filter(TEXTURE_FILTER_NEAREST); alternative_tiles_drawing_root->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); + alternative_tiles_drawing_root->set_texture_filter(TEXTURE_FILTER_NEAREST); alternative_tiles_root_control->add_child(alternative_tiles_drawing_root); alternatives_draw = memnew(Control); - alternatives_draw->connect("draw", callable_mp(this, &TileAtlasView::_draw_alternatives)); alternatives_draw->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); + alternatives_draw->connect("draw", callable_mp(this, &TileAtlasView::_draw_alternatives)); alternative_tiles_drawing_root->add_child(alternatives_draw); } diff --git a/editor/plugins/tiles/tile_atlas_view.h b/editor/plugins/tiles/tile_atlas_view.h index 28fd3ed1e0..bafc2b3985 100644 --- a/editor/plugins/tiles/tile_atlas_view.h +++ b/editor/plugins/tiles/tile_atlas_view.h @@ -34,6 +34,7 @@ #include "editor/editor_zoom_widget.h" #include "scene/gui/box_container.h" #include "scene/gui/button.h" +#include "scene/gui/center_container.h" #include "scene/gui/label.h" #include "scene/gui/margin_container.h" #include "scene/gui/scroll_container.h" @@ -48,17 +49,24 @@ private: TileSetAtlasSource *tile_set_atlas_source; int source_id = -1; + enum DragType { + DRAG_TYPE_NONE, + DRAG_TYPE_PAN, + }; + DragType drag_type = DRAG_TYPE_NONE; float previous_zoom = 1.0; EditorZoomWidget *zoom_widget; + Button *button_center_view; + CenterContainer *center_container; + Vector2 panning; + void _update_zoom_and_panning(bool p_zoom_on_mouse_pos = false); void _zoom_widget_changed(); - void _scroll_changed(); - void _update_zoom(float p_zoom, bool p_zoom_on_mouse_pos = false, Vector2i p_scroll = Vector2i(-1, -1)); + void _center_view(); void _gui_input(const Ref &p_event); Map> alternative_tiles_rect_cache; void _update_alternative_tiles_rect_cache(); - ScrollContainer *scroll_container; MarginContainer *margin_container; int margin_container_paddings[4] = { 0, 0, 0, 0 }; HBoxContainer *hbox; @@ -102,16 +110,15 @@ private: Size2i _compute_alternative_tiles_control_size(); protected: + void _notification(int p_what); static void _bind_methods(); public: // Global. void set_atlas_source(TileSet *p_tile_set, TileSetAtlasSource *p_tile_set_atlas_source, int p_source_id); - ScrollContainer *get_scroll_container() { return scroll_container; }; - float get_zoom() const; - void set_transform(float p_zoom, Vector2i p_scroll); + void set_transform(float p_zoom, Vector2i p_panning); void set_padding(Side p_side, int p_padding); diff --git a/editor/plugins/tiles/tile_data_editors.cpp b/editor/plugins/tiles/tile_data_editors.cpp index 191440bdb3..d9d0e48fb3 100644 --- a/editor/plugins/tiles/tile_data_editors.cpp +++ b/editor/plugins/tiles/tile_data_editors.cpp @@ -32,202 +32,2436 @@ #include "tile_set_editor.h" -TileData *TileDataEditor::_get_tile_data(TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile) { - ERR_FAIL_COND_V(!p_tile_set, nullptr); - ERR_FAIL_COND_V(!p_tile_set->has_source(p_atlas_source_id), nullptr); +#include "core/math/geometry_2d.h" +#include "core/os/keyboard.h" + +#include "editor/editor_properties.h" +#include "editor/editor_scale.h" + +void TileDataEditor::_call_tile_set_changed() { + _tile_set_changed(); +} + +TileData *TileDataEditor::_get_tile_data(TileMapCell p_cell) { + ERR_FAIL_COND_V(!tile_set.is_valid(), nullptr); + ERR_FAIL_COND_V(!tile_set->has_source(p_cell.source_id), nullptr); TileData *td = nullptr; - TileSetSource *source = *p_tile_set->get_source(p_atlas_source_id); + TileSetSource *source = *tile_set->get_source(p_cell.source_id); TileSetAtlasSource *atlas_source = Object::cast_to(source); if (atlas_source) { - ERR_FAIL_COND_V(!atlas_source->has_tile(p_atlas_coords), nullptr); - ERR_FAIL_COND_V(!atlas_source->has_alternative_tile(p_atlas_coords, p_alternative_tile), nullptr); - td = Object::cast_to(atlas_source->get_tile_data(p_atlas_coords, p_alternative_tile)); + ERR_FAIL_COND_V(!atlas_source->has_tile(p_cell.get_atlas_coords()), nullptr); + ERR_FAIL_COND_V(!atlas_source->has_alternative_tile(p_cell.get_atlas_coords(), p_cell.alternative_tile), nullptr); + td = Object::cast_to(atlas_source->get_tile_data(p_cell.get_atlas_coords(), p_cell.alternative_tile)); + } + + return td; +} + +void TileDataEditor::_bind_methods() { + ADD_SIGNAL(MethodInfo("needs_redraw")); +} + +void TileDataEditor::set_tile_set(Ref p_tile_set) { + if (tile_set.is_valid()) { + tile_set->disconnect("changed", callable_mp(this, &TileDataEditor::_call_tile_set_changed)); + } + tile_set = p_tile_set; + if (tile_set.is_valid()) { + tile_set->connect("changed", callable_mp(this, &TileDataEditor::_call_tile_set_changed)); + } + _call_tile_set_changed(); +} + +bool DummyObject::_set(const StringName &p_name, const Variant &p_value) { + if (properties.has(p_name)) { + properties[p_name] = p_value; + return true; + } + return false; +} + +bool DummyObject::_get(const StringName &p_name, Variant &r_ret) const { + if (properties.has(p_name)) { + r_ret = properties[p_name]; + return true; + } + return false; +} + +bool DummyObject::has_dummy_property(StringName p_name) { + return properties.has(p_name); +} + +void DummyObject::add_dummy_property(StringName p_name) { + ERR_FAIL_COND(properties.has(p_name)); + properties[p_name] = Variant(); +} + +void DummyObject::remove_dummy_property(StringName p_name) { + ERR_FAIL_COND(!properties.has(p_name)); + properties.erase(p_name); +} + +void DummyObject::clear_dummy_properties() { + properties.clear(); +} + +void GenericTilePolygonEditor::_base_control_draw() { + ERR_FAIL_COND(!tile_set.is_valid()); + + real_t grab_threshold = EDITOR_GET("editors/poly_editor/point_grab_radius"); + + Color grid_color = EditorSettings::get_singleton()->get("editors/tiles_editor/grid_color"); + const Ref handle = get_theme_icon("EditorPathSharpHandle", "EditorIcons"); + const Ref add_handle = get_theme_icon("EditorHandleAdd", "EditorIcons"); + + Size2 tile_size = tile_set->get_tile_size(); + + Transform2D xform; + xform.set_origin(base_control->get_size() / 2 + panning); + xform.set_scale(Vector2(editor_zoom_widget->get_zoom(), editor_zoom_widget->get_zoom())); + base_control->draw_set_transform_matrix(xform); + + // Draw the tile shape filled. + tile_set->draw_tile_shape(base_control, Rect2(-tile_size / 2, tile_size), Color(1.0, 1.0, 1.0, 0.3), true); + + // Draw the background. + if (background_texture.is_valid()) { + base_control->draw_texture_rect_region(background_texture, Rect2(-background_region.size / 2 - background_offset, background_region.size), background_region, background_modulate, background_transpose); + } + + // Draw the polygons. + for (unsigned int i = 0; i < polygons.size(); i++) { + const Vector &polygon = polygons[i]; + Color color = polygon_color; + if (!in_creation_polygon.is_empty()) { + color = color.darkened(0.3); + } + color.a = 0.5; + Vector v_color; + v_color.push_back(color); + base_control->draw_polygon(polygon, v_color); + + color.a = 0.7; + for (int j = 0; j < polygon.size(); j++) { + base_control->draw_line(polygon[j], polygon[(j + 1) % polygon.size()], color); + } + } + + // Draw the polygon in creation. + if (!in_creation_polygon.is_empty()) { + for (int i = 0; i < in_creation_polygon.size() - 1; i++) { + base_control->draw_line(in_creation_polygon[i], in_creation_polygon[i + 1], Color(1.0, 1.0, 1.0)); + } + } + + Point2 in_creation_point = xform.affine_inverse().xform(base_control->get_local_mouse_position()); + float in_creation_distance = grab_threshold * 2.0; + _snap_to_tile_shape(in_creation_point, in_creation_distance, grab_threshold / editor_zoom_widget->get_zoom()); + if (button_pixel_snap->is_pressed()) { + _snap_to_half_pixel(in_creation_point); + } + + if (drag_type == DRAG_TYPE_CREATE_POINT && !in_creation_polygon.is_empty()) { + base_control->draw_line(in_creation_polygon[in_creation_polygon.size() - 1], in_creation_point, Color(1.0, 1.0, 1.0)); + } + + // Draw the handles. + int tinted_polygon_index = -1; + int tinted_point_index = -1; + if (drag_type == DRAG_TYPE_DRAG_POINT) { + tinted_polygon_index = drag_polygon_index; + tinted_point_index = drag_point_index; + } else if (hovered_point_index >= 0) { + tinted_polygon_index = hovered_polygon_index; + tinted_point_index = hovered_point_index; + } + + base_control->draw_set_transform_matrix(Transform2D()); + if (!in_creation_polygon.is_empty()) { + for (int i = 0; i < in_creation_polygon.size(); i++) { + base_control->draw_texture(handle, xform.xform(in_creation_polygon[i]) - handle->get_size() / 2); + } + } else { + for (int i = 0; i < (int)polygons.size(); i++) { + const Vector &polygon = polygons[i]; + for (int j = 0; j < polygon.size(); j++) { + const Color modulate = (tinted_polygon_index == i && tinted_point_index == j) ? Color(0.5, 1, 2) : Color(1, 1, 1); + base_control->draw_texture(handle, xform.xform(polygon[j]) - handle->get_size() / 2, modulate); + } + } + } + + // Draw the text on top of the selected point. + if (tinted_polygon_index >= 0) { + Ref font = get_theme_font("font", "Label"); + int font_size = get_theme_font_size("font_size", "Label"); + String text = multiple_polygon_mode ? vformat("%d:%d", tinted_polygon_index, tinted_point_index) : vformat("%d", tinted_point_index); + Size2 text_size = font->get_string_size(text, font_size); + base_control->draw_string(font, xform.xform(polygons[tinted_polygon_index][tinted_point_index]) - text_size * 0.5, text, HALIGN_LEFT, -1, font_size, Color(1.0, 1.0, 1.0, 0.5)); + } + + if (drag_type == DRAG_TYPE_CREATE_POINT) { + base_control->draw_texture(handle, xform.xform(in_creation_point) - handle->get_size() / 2, Color(0.5, 1, 2)); + } + + // Draw the point creation preview in edit mode. + if (hovered_segment_index >= 0) { + base_control->draw_texture(add_handle, xform.xform(hovered_segment_point) - add_handle->get_size() / 2); + } + + // Draw the tile shape line. + base_control->draw_set_transform_matrix(xform); + tile_set->draw_tile_shape(base_control, Rect2(-tile_size / 2, tile_size), grid_color, false); + base_control->draw_set_transform_matrix(Transform2D()); +} + +void GenericTilePolygonEditor::_center_view() { + panning = Vector2(); + base_control->update(); + button_center_view->set_disabled(true); +} + +void GenericTilePolygonEditor::_zoom_changed() { + base_control->update(); +} + +void GenericTilePolygonEditor::_advanced_menu_item_pressed(int p_item_pressed) { + switch (p_item_pressed) { + case RESET_TO_DEFAULT_TILE: + undo_redo->create_action(TTR("Edit Polygons")); + undo_redo->add_do_method(this, "clear_polygons"); + undo_redo->add_do_method(this, "add_polygon", tile_set->get_tile_shape_polygon()); + undo_redo->add_do_method(base_control, "update"); + undo_redo->add_do_method(this, "emit_signal", "polygons_changed"); + undo_redo->add_undo_method(this, "clear_polygons"); + for (unsigned int i = 0; i < polygons.size(); i++) { + undo_redo->add_undo_method(this, "add_polygon", polygons[i]); + } + undo_redo->add_undo_method(base_control, "update"); + undo_redo->add_undo_method(this, "emit_signal", "polygons_changed"); + undo_redo->commit_action(true); + break; + case CLEAR_TILE: + undo_redo->create_action(TTR("Edit Polygons")); + undo_redo->add_do_method(this, "clear_polygons"); + undo_redo->add_do_method(base_control, "update"); + undo_redo->add_do_method(this, "emit_signal", "polygons_changed"); + undo_redo->add_undo_method(this, "clear_polygons"); + for (unsigned int i = 0; i < polygons.size(); i++) { + undo_redo->add_undo_method(this, "add_polygon", polygons[i]); + } + undo_redo->add_undo_method(base_control, "update"); + undo_redo->add_undo_method(this, "emit_signal", "polygons_changed"); + undo_redo->commit_action(true); + break; + default: + break; + } +} + +void GenericTilePolygonEditor::_grab_polygon_point(Vector2 p_pos, const Transform2D &p_polygon_xform, int &r_polygon_index, int &r_point_index) { + const real_t grab_threshold = EDITOR_GET("editors/poly_editor/point_grab_radius"); + r_polygon_index = -1; + r_point_index = -1; + float closest_distance = grab_threshold + 1.0; + for (unsigned int i = 0; i < polygons.size(); i++) { + const Vector &polygon = polygons[i]; + for (int j = 0; j < polygon.size(); j++) { + float distance = p_pos.distance_to(p_polygon_xform.xform(polygon[j])); + if (distance < grab_threshold && distance < closest_distance) { + r_polygon_index = i; + r_point_index = j; + closest_distance = distance; + } + } + } +} + +void GenericTilePolygonEditor::_grab_polygon_segment_point(Vector2 p_pos, const Transform2D &p_polygon_xform, int &r_polygon_index, int &r_segment_index, Vector2 &r_point) { + const real_t grab_threshold = EDITOR_GET("editors/poly_editor/point_grab_radius"); + + Point2 point = p_polygon_xform.affine_inverse().xform(p_pos); + r_polygon_index = -1; + r_segment_index = -1; + float closest_distance = grab_threshold * 2.0; + for (unsigned int i = 0; i < polygons.size(); i++) { + const Vector &polygon = polygons[i]; + for (int j = 0; j < polygon.size(); j++) { + Vector2 segment[2] = { polygon[j], polygon[(j + 1) % polygon.size()] }; + Vector2 closest_point = Geometry2D::get_closest_point_to_segment(point, segment); + float distance = closest_point.distance_to(point); + if (distance < grab_threshold / editor_zoom_widget->get_zoom() && distance < closest_distance) { + r_polygon_index = i; + r_segment_index = j; + r_point = closest_point; + closest_distance = distance; + } + } + } +} + +void GenericTilePolygonEditor::_snap_to_tile_shape(Point2 &r_point, float &r_current_snapped_dist, float p_snap_dist) { + ERR_FAIL_COND(!tile_set.is_valid()); + + Vector polygon = tile_set->get_tile_shape_polygon(); + Point2 snapped_point = r_point; + + // Snap to polygon vertices. + bool snapped = false; + for (int i = 0; i < polygon.size(); i++) { + float distance = r_point.distance_to(polygon[i]); + if (distance < p_snap_dist && distance < r_current_snapped_dist) { + snapped_point = polygon[i]; + r_current_snapped_dist = distance; + snapped = true; + } + } + + // Snap to edges if we did not snap to vertices. + if (!snapped) { + for (int i = 0; i < polygon.size(); i++) { + Point2 segment[2] = { polygon[i], polygon[(i + 1) % polygon.size()] }; + Point2 point = Geometry2D::get_closest_point_to_segment(r_point, segment); + float distance = r_point.distance_to(point); + if (distance < p_snap_dist && distance < r_current_snapped_dist) { + snapped_point = point; + r_current_snapped_dist = distance; + } + } + } + + r_point = snapped_point; +} + +void GenericTilePolygonEditor::_snap_to_half_pixel(Point2 &r_point) { + r_point = (r_point * 2).round() / 2.0; +} + +void GenericTilePolygonEditor::_base_control_gui_input(Ref p_event) { + real_t grab_threshold = EDITOR_GET("editors/poly_editor/point_grab_radius"); + + hovered_polygon_index = -1; + hovered_point_index = -1; + hovered_segment_index = -1; + hovered_segment_point = Vector2(); + + Transform2D xform; + xform.set_origin(base_control->get_size() / 2 + panning); + xform.set_scale(Vector2(editor_zoom_widget->get_zoom(), editor_zoom_widget->get_zoom())); + + Ref mm = p_event; + if (mm.is_valid()) { + if (drag_type == DRAG_TYPE_DRAG_POINT) { + ERR_FAIL_INDEX(drag_polygon_index, (int)polygons.size()); + ERR_FAIL_INDEX(drag_point_index, polygons[drag_polygon_index].size()); + Point2 point = xform.affine_inverse().xform(mm->get_position()); + float distance = grab_threshold * 2.0; + _snap_to_tile_shape(point, distance, grab_threshold / editor_zoom_widget->get_zoom()); + if (button_pixel_snap->is_pressed()) { + _snap_to_half_pixel(point); + } + polygons[drag_polygon_index].write[drag_point_index] = point; + } else if (drag_type == DRAG_TYPE_PAN) { + panning += mm->get_position() - drag_last_pos; + drag_last_pos = mm->get_position(); + button_center_view->set_disabled(panning.is_equal_approx(Vector2())); + } else { + // Update hovered point. + _grab_polygon_point(mm->get_position(), xform, hovered_polygon_index, hovered_point_index); + + // If we have no hovered point, check if we hover a segment. + if (hovered_point_index == -1) { + _grab_polygon_segment_point(mm->get_position(), xform, hovered_polygon_index, hovered_segment_index, hovered_segment_point); + } + } + } + + Ref mb = p_event; + if (mb.is_valid()) { + if (mb->get_button_index() == MOUSE_BUTTON_WHEEL_UP && mb->is_ctrl_pressed()) { + editor_zoom_widget->set_zoom_by_increments(1); + _zoom_changed(); + accept_event(); + } else if (mb->get_button_index() == MOUSE_BUTTON_WHEEL_DOWN && mb->is_ctrl_pressed()) { + editor_zoom_widget->set_zoom_by_increments(-1); + _zoom_changed(); + accept_event(); + } else if (mb->get_button_index() == MOUSE_BUTTON_LEFT) { + if (mb->is_pressed()) { + if (tools_button_group->get_pressed_button() != button_create) { + in_creation_polygon.clear(); + } + if (tools_button_group->get_pressed_button() == button_create) { + // Create points. + if (in_creation_polygon.size() >= 3 && mb->get_position().distance_to(xform.xform(in_creation_polygon[0])) < grab_threshold) { + // Closes and create polygon. + if (!multiple_polygon_mode) { + clear_polygons(); + } + int added = add_polygon(in_creation_polygon); + + in_creation_polygon.clear(); + button_edit->set_pressed(true); + undo_redo->create_action(TTR("Edit Polygons")); + if (!multiple_polygon_mode) { + undo_redo->add_do_method(this, "clear_polygons"); + } + undo_redo->add_do_method(this, "add_polygon", in_creation_polygon); + undo_redo->add_do_method(base_control, "update"); + undo_redo->add_undo_method(this, "remove_polygon", added); + undo_redo->add_undo_method(base_control, "update"); + undo_redo->commit_action(false); + emit_signal("polygons_changed"); + } else { + // Create a new point. + drag_type = DRAG_TYPE_CREATE_POINT; + } + } else if (tools_button_group->get_pressed_button() == button_edit) { + // Edit points. + int closest_polygon; + int closest_point; + _grab_polygon_point(mb->get_position(), xform, closest_polygon, closest_point); + if (closest_polygon >= 0) { + drag_type = DRAG_TYPE_DRAG_POINT; + drag_polygon_index = closest_polygon; + drag_point_index = closest_point; + drag_old_polygon = polygons[drag_polygon_index]; + } else { + // Create a point. + Vector2 point_to_create; + _grab_polygon_segment_point(mb->get_position(), xform, closest_polygon, closest_point, point_to_create); + if (closest_polygon >= 0) { + polygons[closest_polygon].insert(closest_point + 1, point_to_create); + drag_type = DRAG_TYPE_DRAG_POINT; + drag_polygon_index = closest_polygon; + drag_point_index = closest_point + 1; + drag_old_polygon = polygons[closest_polygon]; + } + } + } else if (tools_button_group->get_pressed_button() == button_delete) { + // Remove point. + int closest_polygon; + int closest_point; + _grab_polygon_point(mb->get_position(), xform, closest_polygon, closest_point); + if (closest_polygon >= 0) { + PackedVector2Array old_polygon = polygons[closest_polygon]; + polygons[closest_polygon].remove(closest_point); + undo_redo->create_action(TTR("Edit Polygons")); + if (polygons[closest_polygon].size() < 3) { + remove_polygon(closest_polygon); + undo_redo->add_do_method(this, "remove_polygon", closest_polygon); + undo_redo->add_undo_method(this, "add_polygon", old_polygon, closest_polygon); + } else { + undo_redo->add_do_method(this, "set_polygon", closest_polygon, polygons[closest_polygon]); + undo_redo->add_undo_method(this, "set_polygon", closest_polygon, old_polygon); + } + undo_redo->add_do_method(base_control, "update"); + undo_redo->add_undo_method(base_control, "update"); + undo_redo->commit_action(false); + emit_signal("polygons_changed"); + } + } + } else { + if (drag_type == DRAG_TYPE_DRAG_POINT) { + undo_redo->create_action(TTR("Edit Polygons")); + undo_redo->add_do_method(this, "set_polygon", drag_polygon_index, polygons[drag_polygon_index]); + undo_redo->add_do_method(base_control, "update"); + undo_redo->add_undo_method(this, "set_polygon", drag_polygon_index, drag_old_polygon); + undo_redo->add_undo_method(base_control, "update"); + undo_redo->commit_action(false); + emit_signal("polygons_changed"); + } else if (drag_type == DRAG_TYPE_CREATE_POINT) { + Point2 point = xform.affine_inverse().xform(mb->get_position()); + float distance = grab_threshold * 2; + _snap_to_tile_shape(point, distance, grab_threshold / editor_zoom_widget->get_zoom()); + if (button_pixel_snap->is_pressed()) { + _snap_to_half_pixel(point); + } + in_creation_polygon.push_back(point); + } + drag_type = DRAG_TYPE_NONE; + drag_point_index = -1; + } + + } else if (mb->get_button_index() == MOUSE_BUTTON_RIGHT) { + if (mb->is_pressed()) { + if (tools_button_group->get_pressed_button() == button_edit) { + // Remove point or pan. + int closest_polygon; + int closest_point; + _grab_polygon_point(mb->get_position(), xform, closest_polygon, closest_point); + if (closest_polygon >= 0) { + PackedVector2Array old_polygon = polygons[closest_polygon]; + polygons[closest_polygon].remove(closest_point); + undo_redo->create_action(TTR("Edit Polygons")); + if (polygons[closest_polygon].size() < 3) { + remove_polygon(closest_polygon); + undo_redo->add_do_method(this, "remove_polygon", closest_polygon); + undo_redo->add_undo_method(this, "add_polygon", old_polygon, closest_polygon); + } else { + undo_redo->add_do_method(this, "set_polygon", closest_polygon, polygons[closest_polygon]); + undo_redo->add_undo_method(this, "set_polygon", closest_polygon, old_polygon); + } + undo_redo->add_do_method(base_control, "update"); + undo_redo->add_undo_method(base_control, "update"); + undo_redo->commit_action(false); + emit_signal("polygons_changed"); + } else { + drag_type = DRAG_TYPE_PAN; + drag_last_pos = mb->get_position(); + } + } else { + drag_type = DRAG_TYPE_PAN; + drag_last_pos = mb->get_position(); + } + } else { + drag_type = DRAG_TYPE_NONE; + } + } else if (mb->get_button_index() == MOUSE_BUTTON_MIDDLE) { + if (mb->is_pressed()) { + drag_type = DRAG_TYPE_PAN; + drag_last_pos = mb->get_position(); + } else { + drag_type = DRAG_TYPE_NONE; + } + } + } + + base_control->update(); +} + +void GenericTilePolygonEditor::set_tile_set(Ref p_tile_set) { + if (tile_set != p_tile_set) { + // Set the default tile shape + clear_polygons(); + if (p_tile_set.is_valid()) { + add_polygon(p_tile_set->get_tile_shape_polygon()); + } + } + tile_set = p_tile_set; +} + +void GenericTilePolygonEditor::set_background(Ref p_texture, Rect2 p_region, Vector2 p_offset, bool p_flip_h, bool p_flip_v, bool p_transpose, Color p_modulate) { + background_texture = p_texture; + background_region = p_region; + background_offset = p_offset; + background_h_flip = p_flip_h; + background_v_flip = p_flip_v; + background_transpose = p_transpose; + background_modulate = p_modulate; + base_control->update(); +} + +int GenericTilePolygonEditor::get_polygon_count() { + return polygons.size(); +} + +int GenericTilePolygonEditor::add_polygon(Vector p_polygon, int p_index) { + ERR_FAIL_COND_V(p_polygon.size() < 3, -1); + ERR_FAIL_COND_V(!multiple_polygon_mode && polygons.size() >= 1, -1); + + if (p_index < 0) { + polygons.push_back(p_polygon); + base_control->update(); + button_edit->set_pressed(true); + return polygons.size() - 1; + } else { + polygons.insert(p_index, p_polygon); + button_edit->set_pressed(true); + base_control->update(); + return p_index; + } +} + +void GenericTilePolygonEditor::remove_polygon(int p_index) { + ERR_FAIL_INDEX(p_index, (int)polygons.size()); + polygons.remove(p_index); + + if (polygons.size() == 0) { + button_create->set_pressed(true); + } + base_control->update(); +} + +void GenericTilePolygonEditor::clear_polygons() { + polygons.clear(); + base_control->update(); +} + +void GenericTilePolygonEditor::set_polygon(int p_polygon_index, Vector p_polygon) { + ERR_FAIL_INDEX(p_polygon_index, (int)polygons.size()); + ERR_FAIL_COND(p_polygon.size() < 3); + polygons[p_polygon_index] = p_polygon; + button_edit->set_pressed(true); + base_control->update(); +} + +Vector GenericTilePolygonEditor::get_polygon(int p_polygon_index) { + ERR_FAIL_INDEX_V(p_polygon_index, (int)polygons.size(), Vector()); + return polygons[p_polygon_index]; +} + +void GenericTilePolygonEditor::set_polygons_color(Color p_color) { + polygon_color = p_color; + base_control->update(); +} + +void GenericTilePolygonEditor::set_multiple_polygon_mode(bool p_multiple_polygon_mode) { + multiple_polygon_mode = p_multiple_polygon_mode; +} + +void GenericTilePolygonEditor::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_READY: + button_create->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon("CurveCreate", "EditorIcons")); + button_edit->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon("CurveEdit", "EditorIcons")); + button_delete->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon("CurveDelete", "EditorIcons")); + button_center_view->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon("CenterView", "EditorIcons")); + button_pixel_snap->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon("Snap", "EditorIcons")); + button_advanced_menu->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon("GuiTabMenu", "EditorIcons")); + break; + } +} + +void GenericTilePolygonEditor::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_polygon_count"), &GenericTilePolygonEditor::get_polygon_count); + ClassDB::bind_method(D_METHOD("add_polygon", "polygon", "index"), &GenericTilePolygonEditor::add_polygon, DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("remove_polygon", "index"), &GenericTilePolygonEditor::remove_polygon); + ClassDB::bind_method(D_METHOD("clear_polygons"), &GenericTilePolygonEditor::clear_polygons); + ClassDB::bind_method(D_METHOD("set_polygon", "index", "polygon"), &GenericTilePolygonEditor::set_polygon); + ClassDB::bind_method(D_METHOD("get_polygon", "index"), &GenericTilePolygonEditor::set_polygon); + + ADD_SIGNAL(MethodInfo("polygons_changed")); +} + +GenericTilePolygonEditor::GenericTilePolygonEditor() { + toolbar = memnew(HBoxContainer); + add_child(toolbar); + + tools_button_group.instantiate(); + + button_create = memnew(Button); + button_create->set_flat(true); + button_create->set_toggle_mode(true); + button_create->set_button_group(tools_button_group); + button_create->set_pressed(true); + toolbar->add_child(button_create); + + button_edit = memnew(Button); + button_edit->set_flat(true); + button_edit->set_toggle_mode(true); + button_edit->set_button_group(tools_button_group); + toolbar->add_child(button_edit); + + button_delete = memnew(Button); + button_delete->set_flat(true); + button_delete->set_toggle_mode(true); + button_delete->set_button_group(tools_button_group); + toolbar->add_child(button_delete); + + button_advanced_menu = memnew(MenuButton); + button_advanced_menu->set_flat(true); + button_advanced_menu->set_toggle_mode(true); + button_advanced_menu->get_popup()->add_item(TTR("Reset to default tile shape"), RESET_TO_DEFAULT_TILE); + button_advanced_menu->get_popup()->add_item(TTR("Clear"), CLEAR_TILE); + button_advanced_menu->get_popup()->connect("id_pressed", callable_mp(this, &GenericTilePolygonEditor::_advanced_menu_item_pressed)); + toolbar->add_child(button_advanced_menu); + + toolbar->add_child(memnew(VSeparator)); + + button_pixel_snap = memnew(Button); + button_pixel_snap->set_flat(true); + button_pixel_snap->set_toggle_mode(true); + button_pixel_snap->set_pressed(true); + toolbar->add_child(button_pixel_snap); + + Control *root = memnew(Control); + root->set_h_size_flags(Control::SIZE_EXPAND_FILL); + root->set_custom_minimum_size(Size2(0, 200 * EDSCALE)); + root->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); + add_child(root); + + panel = memnew(Panel); + panel->set_anchors_and_offsets_preset(Control::PRESET_WIDE); + panel->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); + root->add_child(panel); + + base_control = memnew(Control); + base_control->set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST); + base_control->set_anchors_and_offsets_preset(Control::PRESET_WIDE); + base_control->connect("draw", callable_mp(this, &GenericTilePolygonEditor::_base_control_draw)); + base_control->connect("gui_input", callable_mp(this, &GenericTilePolygonEditor::_base_control_gui_input)); + base_control->set_clip_contents(true); + root->add_child(base_control); + + editor_zoom_widget = memnew(EditorZoomWidget); + editor_zoom_widget->set_position(Vector2(5, 5)); + editor_zoom_widget->connect("zoom_changed", callable_mp(this, &GenericTilePolygonEditor::_zoom_changed).unbind(1)); + root->add_child(editor_zoom_widget); + + button_center_view = memnew(Button); + button_center_view->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon("CenterView", "EditorIcons")); + button_center_view->set_anchors_and_offsets_preset(Control::PRESET_TOP_RIGHT, Control::PRESET_MODE_MINSIZE, 5); + button_center_view->connect("pressed", callable_mp(this, &GenericTilePolygonEditor::_center_view)); + button_center_view->set_flat(true); + button_center_view->set_disabled(true); + root->add_child(button_center_view); +} + +void TileDataDefaultEditor::_property_value_changed(StringName p_property, Variant p_value, StringName p_field) { + ERR_FAIL_COND(!dummy_object); + dummy_object->set(p_property, p_value); +} + +Variant TileDataDefaultEditor::_get_painted_value() { + ERR_FAIL_COND_V(!dummy_object, Variant()); + return dummy_object->get(property); +} + +void TileDataDefaultEditor::_set_painted_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) { + TileData *tile_data = Object::cast_to(p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile)); + ERR_FAIL_COND(!tile_data); + Variant value = tile_data->get(property); + dummy_object->set(property, value); + if (property_editor) { + property_editor->update_property(); + } +} + +void TileDataDefaultEditor::_set_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile, Variant p_value) { + TileData *tile_data = Object::cast_to(p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile)); + ERR_FAIL_COND(!tile_data); + tile_data->set(property, p_value); +} + +Variant TileDataDefaultEditor::_get_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) { + TileData *tile_data = Object::cast_to(p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile)); + ERR_FAIL_COND_V(!tile_data, Variant()); + return tile_data->get(property); +} + +void TileDataDefaultEditor::_setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, Map p_previous_values, Variant p_new_value) { + for (Map::Element *E = p_previous_values.front(); E; E = E->next()) { + Vector2i coords = E->key().get_atlas_coords(); + undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/%s", coords.x, coords.y, E->key().alternative_tile, property), E->get()); + undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/%s", coords.x, coords.y, E->key().alternative_tile, property), p_new_value); + } +} + +void TileDataDefaultEditor::forward_draw_over_atlas(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_set_atlas_source, CanvasItem *p_canvas_item, Transform2D p_transform) { + if (drag_type == DRAG_TYPE_PAINT_RECT) { + Color grid_color = EditorSettings::get_singleton()->get("editors/tiles_editor/grid_color"); + Color selection_color = Color().from_hsv(Math::fposmod(grid_color.get_h() + 0.5, 1.0), grid_color.get_s(), grid_color.get_v(), 1.0); + + p_canvas_item->draw_set_transform_matrix(p_transform); + + Rect2i rect; + rect.set_position(p_tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_pos)); + rect.set_end(p_tile_atlas_view->get_atlas_tile_coords_at_pos(p_transform.affine_inverse().xform(p_canvas_item->get_local_mouse_position()))); + rect = rect.abs(); + + Set edited; + for (int x = rect.get_position().x; x <= rect.get_end().x; x++) { + for (int y = rect.get_position().y; y <= rect.get_end().y; y++) { + Vector2i coords = Vector2i(x, y); + coords = p_tile_set_atlas_source->get_tile_at_coords(coords); + if (coords != TileSetSource::INVALID_ATLAS_COORDS) { + TileMapCell cell; + cell.source_id = 0; + cell.set_atlas_coords(coords); + cell.alternative_tile = 0; + edited.insert(cell); + } + } + } + + for (Set::Element *E = edited.front(); E; E = E->next()) { + Vector2i coords = E->get().get_atlas_coords(); + p_canvas_item->draw_rect(p_tile_set_atlas_source->get_tile_texture_region(coords), selection_color, false); + } + p_canvas_item->draw_set_transform_matrix(Transform2D()); + } +}; + +void TileDataDefaultEditor::forward_draw_over_alternatives(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_set_atlas_source, CanvasItem *p_canvas_item, Transform2D p_transform){ + +}; + +void TileDataDefaultEditor::forward_painting_atlas_gui_input(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_set_atlas_source, const Ref &p_event) { + Ref mm = p_event; + if (mm.is_valid()) { + if (drag_type == DRAG_TYPE_PAINT) { + Vector line = Geometry2D::bresenham_line(p_tile_atlas_view->get_atlas_tile_coords_at_pos(drag_last_pos), p_tile_atlas_view->get_atlas_tile_coords_at_pos(mm->get_position())); + for (int i = 0; i < line.size(); i++) { + Vector2i coords = p_tile_set_atlas_source->get_tile_at_coords(line[i]); + if (coords != TileSetSource::INVALID_ATLAS_COORDS) { + TileMapCell cell; + cell.source_id = 0; + cell.set_atlas_coords(coords); + cell.alternative_tile = 0; + if (!drag_modified.has(cell)) { + drag_modified[cell] = _get_value(p_tile_set_atlas_source, coords, 0); + } + _set_value(p_tile_set_atlas_source, coords, 0, drag_painted_value); + } + } + drag_last_pos = mm->get_position(); + } + } + + Ref mb = p_event; + if (mb.is_valid()) { + if (mb->get_button_index() == MOUSE_BUTTON_LEFT) { + if (mb->is_pressed()) { + if (picker_button->is_pressed()) { + Vector2i coords = p_tile_atlas_view->get_atlas_tile_coords_at_pos(mb->get_position()); + coords = p_tile_set_atlas_source->get_tile_at_coords(coords); + if (coords != TileSetSource::INVALID_ATLAS_COORDS) { + _set_painted_value(p_tile_set_atlas_source, coords, 0); + picker_button->set_pressed(false); + } + } else if (mb->is_ctrl_pressed()) { + drag_type = DRAG_TYPE_PAINT_RECT; + drag_modified.clear(); + drag_painted_value = _get_painted_value(); + drag_start_pos = mb->get_position(); + } else { + drag_type = DRAG_TYPE_PAINT; + drag_modified.clear(); + drag_painted_value = _get_painted_value(); + Vector2i coords = p_tile_atlas_view->get_atlas_tile_coords_at_pos(mb->get_position()); + coords = p_tile_set_atlas_source->get_tile_at_coords(coords); + if (coords != TileSetSource::INVALID_ATLAS_COORDS) { + TileMapCell cell; + cell.source_id = 0; + cell.set_atlas_coords(coords); + cell.alternative_tile = 0; + drag_modified[cell] = _get_value(p_tile_set_atlas_source, coords, 0); + _set_value(p_tile_set_atlas_source, coords, 0, drag_painted_value); + } + drag_last_pos = mb->get_position(); + } + } else { + if (drag_type == DRAG_TYPE_PAINT_RECT) { + Rect2i rect; + rect.set_position(p_tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_pos)); + rect.set_end(p_tile_atlas_view->get_atlas_tile_coords_at_pos(mb->get_position())); + rect = rect.abs(); + + drag_modified.clear(); + for (int x = rect.get_position().x; x <= rect.get_end().x; x++) { + for (int y = rect.get_position().y; y <= rect.get_end().y; y++) { + Vector2i coords = Vector2i(x, y); + coords = p_tile_set_atlas_source->get_tile_at_coords(coords); + if (coords != TileSetSource::INVALID_ATLAS_COORDS) { + TileMapCell cell; + cell.source_id = 0; + cell.set_atlas_coords(coords); + cell.alternative_tile = 0; + drag_modified[cell] = _get_value(p_tile_set_atlas_source, coords, 0); + } + } + } + undo_redo->create_action(TTR("Painting Tiles Property")); + _setup_undo_redo_action(p_tile_set_atlas_source, drag_modified, drag_painted_value); + undo_redo->commit_action(true); + drag_type = DRAG_TYPE_NONE; + } else if (drag_type == DRAG_TYPE_PAINT) { + undo_redo->create_action(TTR("Painting Tiles Property")); + _setup_undo_redo_action(p_tile_set_atlas_source, drag_modified, drag_painted_value); + undo_redo->commit_action(false); + drag_type = DRAG_TYPE_NONE; + } + } + } + } +} + +void TileDataDefaultEditor::forward_painting_alternatives_gui_input(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_set_atlas_source, const Ref &p_event) { + Ref mm = p_event; + if (mm.is_valid()) { + if (drag_type == DRAG_TYPE_PAINT) { + Vector3i tile = p_tile_atlas_view->get_alternative_tile_at_pos(mm->get_position()); + Vector2i coords = Vector2i(tile.x, tile.y); + int alternative_tile = tile.z; + + if (coords != TileSetSource::INVALID_ATLAS_COORDS) { + TileMapCell cell; + cell.source_id = 0; + cell.set_atlas_coords(coords); + cell.alternative_tile = alternative_tile; + if (!drag_modified.has(cell)) { + drag_modified[cell] = _get_value(p_tile_set_atlas_source, coords, alternative_tile); + } + _set_value(p_tile_set_atlas_source, coords, alternative_tile, drag_painted_value); + } + + drag_last_pos = mm->get_position(); + } + } + + Ref mb = p_event; + if (mb.is_valid()) { + if (mb->get_button_index() == MOUSE_BUTTON_LEFT) { + if (mb->is_pressed()) { + if (picker_button->is_pressed()) { + Vector3i tile = p_tile_atlas_view->get_alternative_tile_at_pos(mb->get_position()); + Vector2i coords = Vector2i(tile.x, tile.y); + int alternative_tile = tile.z; + if (coords != TileSetSource::INVALID_ATLAS_COORDS) { + _set_painted_value(p_tile_set_atlas_source, coords, alternative_tile); + picker_button->set_pressed(false); + } + } else { + drag_type = DRAG_TYPE_PAINT; + drag_modified.clear(); + drag_painted_value = _get_painted_value(); + + Vector3i tile = p_tile_atlas_view->get_alternative_tile_at_pos(mb->get_position()); + Vector2i coords = Vector2i(tile.x, tile.y); + int alternative_tile = tile.z; + + if (coords != TileSetSource::INVALID_ATLAS_COORDS) { + TileMapCell cell; + cell.source_id = 0; + cell.set_atlas_coords(coords); + cell.alternative_tile = alternative_tile; + drag_modified[cell] = _get_value(p_tile_set_atlas_source, coords, alternative_tile); + _set_value(p_tile_set_atlas_source, coords, alternative_tile, drag_painted_value); + } + drag_last_pos = mb->get_position(); + } + } else { + undo_redo->create_action(TTR("Painting Tiles Property")); + _setup_undo_redo_action(p_tile_set_atlas_source, drag_modified, drag_painted_value); + undo_redo->commit_action(false); + drag_type = DRAG_TYPE_NONE; + } + } + } +} + +void TileDataDefaultEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected) { + TileData *tile_data = _get_tile_data(p_cell); + ERR_FAIL_COND(!tile_data); + + bool valid; + Variant value = tile_data->get(property, &valid); + if (!valid) { + return; + } + + if (value.get_type() == Variant::BOOL) { + Ref texture = (bool)value ? tile_bool_checked : tile_bool_unchecked; + int size = MIN(tile_set->get_tile_size().x, tile_set->get_tile_size().y) / 3; + Rect2 rect = p_transform.xform(Rect2(Vector2(-size / 2, -size / 2), Vector2(size, size))); + p_canvas_item->draw_texture_rect(texture, rect); + } else if (value.get_type() == Variant::COLOR) { + int size = MIN(tile_set->get_tile_size().x, tile_set->get_tile_size().y) / 3; + Rect2 rect = p_transform.xform(Rect2(Vector2(-size / 2, -size / 2), Vector2(size, size))); + p_canvas_item->draw_rect(rect, value); + } else { + Ref font = TileSetEditor::get_singleton()->get_theme_font("bold", "EditorFonts"); + String text; + switch (value.get_type()) { + case Variant::INT: + text = vformat("%d", value); + break; + case Variant::FLOAT: + text = vformat("%.2f", value); + break; + case Variant::STRING: + case Variant::STRING_NAME: + text = value; + break; + default: + return; + break; + } + + Color color = Color(1, 1, 1); + if (p_selected) { + Color grid_color = EditorSettings::get_singleton()->get("editors/tiles_editor/grid_color"); + Color selection_color = Color().from_hsv(Math::fposmod(grid_color.get_h() + 0.5, 1.0), grid_color.get_s(), grid_color.get_v(), 1.0); + selection_color.set_v(0.9); + color = selection_color; + } else if (is_visible_in_tree()) { + Variant painted_value = _get_painted_value(); + bool equal = (painted_value.get_type() == Variant::FLOAT && value.get_type() == Variant::FLOAT) ? Math::is_equal_approx(float(painted_value), float(value)) : painted_value == value; + if (equal) { + color = Color(0.7, 0.7, 0.7); + } + } + + Vector2 string_size = font->get_string_size(text); + p_canvas_item->draw_string(font, p_transform.get_origin() + Vector2i(-string_size.x / 2, string_size.y / 2), text, HALIGN_CENTER, string_size.x, -1, color, 1, Color(0, 0, 0, 1)); + } +} + +void TileDataDefaultEditor::setup_property_editor(Variant::Type p_type, String p_property, String p_label, Variant p_default_value) { + ERR_FAIL_COND_MSG(!property.is_empty(), "Cannot setup TileDataDefaultEditor twice"); + property = p_property; + + // Update everything. + if (property_editor) { + property_editor->queue_delete(); + } + + // Update the dummy object. + dummy_object->add_dummy_property(p_property); + + // Get the default value for the type. + if (p_default_value == Variant()) { + Callable::CallError error; + Variant painted_value; + Variant::construct(p_type, painted_value, nullptr, 0, error); + dummy_object->set(p_property, painted_value); + } else { + dummy_object->set(p_property, p_default_value); + } + + // Create and setup the property editor. + property_editor = EditorInspectorDefaultPlugin::get_editor_for_property(dummy_object, p_type, p_property, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT); + property_editor->set_object_and_property(dummy_object, p_property); + if (p_label.is_empty()) { + property_editor->set_label(p_property); + } else { + property_editor->set_label(p_label); + } + property_editor->connect("property_changed", callable_mp(this, &TileDataDefaultEditor::_property_value_changed).unbind(1)); + property_editor->update_property(); + add_child(property_editor); +} + +void TileDataDefaultEditor::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: + case NOTIFICATION_THEME_CHANGED: + picker_button->set_icon(get_theme_icon("ColorPick", "EditorIcons")); + tile_bool_checked = get_theme_icon("TileChecked", "EditorIcons"); + tile_bool_unchecked = get_theme_icon("TileUnchecked", "EditorIcons"); + break; + default: + break; + } +} + +TileDataDefaultEditor::TileDataDefaultEditor() { + label = memnew(Label); + label->set_text("Painting:"); + add_child(label); + + toolbar->add_child(memnew(VSeparator)); + + picker_button = memnew(Button); + picker_button->set_flat(true); + picker_button->set_toggle_mode(true); + picker_button->set_shortcut(ED_SHORTCUT("tiles_editor/picker", "Picker", KEY_P)); + toolbar->add_child(picker_button); +} + +TileDataDefaultEditor::~TileDataDefaultEditor() { + toolbar->queue_delete(); + memdelete(dummy_object); +} + +void TileDataTextureOffsetEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected) { + TileData *tile_data = _get_tile_data(p_cell); + ERR_FAIL_COND(!tile_data); + + Vector2i tile_set_tile_size = tile_set->get_tile_size(); + Rect2i rect = Rect2i(-tile_set_tile_size / 2, tile_set_tile_size); + Color color = Color(1.0, 0.0, 0.0); + if (p_selected) { + Color grid_color = EditorSettings::get_singleton()->get("editors/tiles_editor/grid_color"); + Color selection_color = Color().from_hsv(Math::fposmod(grid_color.get_h() + 0.5, 1.0), grid_color.get_s(), grid_color.get_v(), 1.0); + color = selection_color; + } + tile_set->draw_tile_shape(p_canvas_item, p_transform.xform(rect), color); +} + +void TileDataPositionEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected) { + TileData *tile_data = _get_tile_data(p_cell); + ERR_FAIL_COND(!tile_data); + + bool valid; + Variant value = tile_data->get(property, &valid); + if (!valid) { + return; + } + ERR_FAIL_COND(value.get_type() != Variant::VECTOR2I && value.get_type() != Variant::VECTOR2); + + Color color = Color(1.0, 1.0, 1.0); + if (p_selected) { + Color grid_color = EditorSettings::get_singleton()->get("editors/tiles_editor/grid_color"); + Color selection_color = Color().from_hsv(Math::fposmod(grid_color.get_h() + 0.5, 1.0), grid_color.get_s(), grid_color.get_v(), 1.0); + color = selection_color; + } + Ref position_icon = TileSetEditor::get_singleton()->get_theme_icon("EditorPosition", "EditorIcons"); + p_canvas_item->draw_texture(position_icon, p_transform.xform(Vector2(value)) - position_icon->get_size() / 2, color); +} + +void TileDataYSortEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected) { + TileData *tile_data = _get_tile_data(p_cell); + ERR_FAIL_COND(!tile_data); + + Color color = Color(1.0, 1.0, 1.0); + if (p_selected) { + Color grid_color = EditorSettings::get_singleton()->get("editors/tiles_editor/grid_color"); + Color selection_color = Color().from_hsv(Math::fposmod(grid_color.get_h() + 0.5, 1.0), grid_color.get_s(), grid_color.get_v(), 1.0); + color = selection_color; + } + Ref position_icon = TileSetEditor::get_singleton()->get_theme_icon("EditorPosition", "EditorIcons"); + p_canvas_item->draw_texture(position_icon, p_transform.xform(Vector2(0, tile_data->get_y_sort_origin())) - position_icon->get_size() / 2, color); +} + +void TileDataOcclusionShapeEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected) { + TileData *tile_data = _get_tile_data(p_cell); + ERR_FAIL_COND(!tile_data); + + Color grid_color = EditorSettings::get_singleton()->get("editors/tiles_editor/grid_color"); + Color selection_color = Color().from_hsv(Math::fposmod(grid_color.get_h() + 0.5, 1.0), grid_color.get_s(), grid_color.get_v(), 1.0); + Color color = grid_color.darkened(0.2); + if (p_selected) { + color = selection_color.darkened(0.2); + } + color.a *= 0.5; + + Vector debug_occlusion_color; + debug_occlusion_color.push_back(color); + + RenderingServer::get_singleton()->canvas_item_add_set_transform(p_canvas_item->get_canvas_item(), p_transform); + Ref occluder = tile_data->get_occluder(occlusion_layer); + if (occluder.is_valid() && occluder->get_polygon().size() >= 3) { + p_canvas_item->draw_polygon(Variant(occluder->get_polygon()), debug_occlusion_color); + } + RenderingServer::get_singleton()->canvas_item_add_set_transform(p_canvas_item->get_canvas_item(), Transform2D()); +} + +Variant TileDataOcclusionShapeEditor::_get_painted_value() { + Ref occluder_polygon; + occluder_polygon.instantiate(); + if (polygon_editor->get_polygon_count() >= 1) { + occluder_polygon->set_polygon(polygon_editor->get_polygon(0)); + } + return occluder_polygon; +} + +void TileDataOcclusionShapeEditor::_set_painted_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) { + TileData *tile_data = Object::cast_to(p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile)); + ERR_FAIL_COND(!tile_data); + + Ref occluder_polygon = tile_data->get_occluder(occlusion_layer); + polygon_editor->clear_polygons(); + if (occluder_polygon.is_valid()) { + polygon_editor->add_polygon(occluder_polygon->get_polygon()); + } + polygon_editor->set_background(p_tile_set_atlas_source->get_texture(), p_tile_set_atlas_source->get_tile_texture_region(p_coords), p_tile_set_atlas_source->get_tile_effective_texture_offset(p_coords, p_alternative_tile), tile_data->get_flip_h(), tile_data->get_flip_v(), tile_data->get_transpose(), tile_data->get_modulate()); +} + +void TileDataOcclusionShapeEditor::_set_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile, Variant p_value) { + TileData *tile_data = Object::cast_to(p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile)); + ERR_FAIL_COND(!tile_data); + Ref occluder_polygon = p_value; + tile_data->set_occluder(occlusion_layer, occluder_polygon); + + polygon_editor->set_background(p_tile_set_atlas_source->get_texture(), p_tile_set_atlas_source->get_tile_texture_region(p_coords), p_tile_set_atlas_source->get_tile_effective_texture_offset(p_coords, p_alternative_tile), tile_data->get_flip_h(), tile_data->get_flip_v(), tile_data->get_transpose(), tile_data->get_modulate()); +} + +Variant TileDataOcclusionShapeEditor::_get_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) { + TileData *tile_data = Object::cast_to(p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile)); + ERR_FAIL_COND_V(!tile_data, Variant()); + return tile_data->get_occluder(occlusion_layer); +} + +void TileDataOcclusionShapeEditor::_setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, Map p_previous_values, Variant p_new_value) { + for (Map::Element *E = p_previous_values.front(); E; E = E->next()) { + Vector2i coords = E->key().get_atlas_coords(); + undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/occlusion_layer_%d/polygon", coords.x, coords.y, E->key().alternative_tile, occlusion_layer), E->get()); + undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/occlusion_layer_%d/polygon", coords.x, coords.y, E->key().alternative_tile, occlusion_layer), p_new_value); + } +} + +void TileDataOcclusionShapeEditor::_tile_set_changed() { + polygon_editor->set_tile_set(tile_set); +} + +void TileDataOcclusionShapeEditor::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: + polygon_editor->set_polygons_color(get_tree()->get_debug_collisions_color()); + break; + default: + break; + } +} + +TileDataOcclusionShapeEditor::TileDataOcclusionShapeEditor() { + polygon_editor = memnew(GenericTilePolygonEditor); + add_child(polygon_editor); +} + +void TileDataCollisionEditor::_property_value_changed(StringName p_property, Variant p_value, StringName p_field) { + dummy_object->set(p_property, p_value); +} + +void TileDataCollisionEditor::_polygons_changed() { + // Update the dummy object properties and their editors. + for (int i = 0; i < polygon_editor->get_polygon_count(); i++) { + StringName one_way_property = vformat("polygon_%d_one_way", i); + StringName one_way_margin_property = vformat("polygon_%d_one_way_margin", i); + + if (!dummy_object->has_dummy_property(one_way_property)) { + dummy_object->add_dummy_property(one_way_property); + dummy_object->set(one_way_property, false); + } + + if (!dummy_object->has_dummy_property(one_way_margin_property)) { + dummy_object->add_dummy_property(one_way_margin_property); + dummy_object->set(one_way_margin_property, 1.0); + } + + if (!property_editors.has(one_way_property)) { + EditorProperty *one_way_property_editor = EditorInspectorDefaultPlugin::get_editor_for_property(dummy_object, Variant::BOOL, one_way_property, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT); + one_way_property_editor->set_object_and_property(dummy_object, one_way_property); + one_way_property_editor->set_label(one_way_property); + one_way_property_editor->connect("property_changed", callable_mp(this, &TileDataCollisionEditor::_property_value_changed).unbind(1)); + one_way_property_editor->update_property(); + add_child(one_way_property_editor); + property_editors[one_way_property] = one_way_property_editor; + } + + if (!property_editors.has(one_way_margin_property)) { + EditorProperty *one_way_margin_property_editor = EditorInspectorDefaultPlugin::get_editor_for_property(dummy_object, Variant::FLOAT, one_way_margin_property, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT); + one_way_margin_property_editor->set_object_and_property(dummy_object, one_way_margin_property); + one_way_margin_property_editor->set_label(one_way_margin_property); + one_way_margin_property_editor->connect("property_changed", callable_mp(this, &TileDataCollisionEditor::_property_value_changed).unbind(1)); + one_way_margin_property_editor->update_property(); + add_child(one_way_margin_property_editor); + property_editors[one_way_margin_property] = one_way_margin_property_editor; + } } - return td; + // Remove uneeded properties and their editors. + for (int i = polygon_editor->get_polygon_count(); dummy_object->has_dummy_property(vformat("polygon_%d_one_way", i)); i++) { + dummy_object->remove_dummy_property(vformat("polygon_%d_one_way", i)); + } + for (int i = polygon_editor->get_polygon_count(); dummy_object->has_dummy_property(vformat("polygon_%d_one_way_margin", i)); i++) { + dummy_object->remove_dummy_property(vformat("polygon_%d_one_way_margin", i)); + } + for (int i = polygon_editor->get_polygon_count(); property_editors.has(vformat("polygon_%d_one_way", i)); i++) { + property_editors[vformat("polygon_%d_one_way", i)]->queue_delete(); + property_editors.erase(vformat("polygon_%d_one_way", i)); + } + for (int i = polygon_editor->get_polygon_count(); property_editors.has(vformat("polygon_%d_one_way_margin", i)); i++) { + property_editors[vformat("polygon_%d_one_way_margin", i)]->queue_delete(); + property_editors.erase(vformat("polygon_%d_one_way_margin", i)); + } } -void TileDataEditor::edit(TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) { +Variant TileDataCollisionEditor::_get_painted_value() { + Array array; + for (int i = 0; i < polygon_editor->get_polygon_count(); i++) { + ERR_FAIL_COND_V(polygon_editor->get_polygon(i).size() < 3, Variant()); + Dictionary dict; + dict["points"] = polygon_editor->get_polygon(i); + dict["one_way"] = dummy_object->get(vformat("polygon_%d_one_way", i)); + dict["one_way_margin"] = dummy_object->get(vformat("polygon_%d_one_way_margin", i)); + array.push_back(dict); + } + + return array; } -void TileDataTextureOffsetEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) { - TileData *tile_data = _get_tile_data(p_tile_set, p_atlas_source_id, p_atlas_coords, p_alternative_tile); +void TileDataCollisionEditor::_set_painted_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) { + TileData *tile_data = Object::cast_to(p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile)); ERR_FAIL_COND(!tile_data); - bool valid; - Variant value = tile_data->get(p_property, &valid); - if (!valid) { - return; + polygon_editor->clear_polygons(); + for (int i = 0; i < tile_data->get_collision_polygons_count(physics_layer); i++) { + Vector polygon = tile_data->get_collision_polygon_points(physics_layer, i); + if (polygon.size() >= 3) { + polygon_editor->add_polygon(polygon); + } } - ERR_FAIL_COND(value.get_type() != Variant::VECTOR2I); - Vector2i tile_set_tile_size = p_tile_set->get_tile_size(); - Rect2i rect = Rect2i(-tile_set_tile_size / 2, tile_set_tile_size); - p_tile_set->draw_tile_shape(p_canvas_item, p_transform.xform(rect), Color(1.0, 0.0, 0.0)); + _polygons_changed(); + for (int i = 0; i < tile_data->get_collision_polygons_count(physics_layer); i++) { + dummy_object->set(vformat("polygon_%d_one_way", i), tile_data->is_collision_polygon_one_way(physics_layer, i)); + dummy_object->set(vformat("polygon_%d_one_way_margin", i), tile_data->get_collision_polygon_one_way_margin(physics_layer, i)); + } + for (Map::Element *E = property_editors.front(); E; E = E->next()) { + E->get()->update_property(); + } + + polygon_editor->set_background(p_tile_set_atlas_source->get_texture(), p_tile_set_atlas_source->get_tile_texture_region(p_coords), p_tile_set_atlas_source->get_tile_effective_texture_offset(p_coords, p_alternative_tile), tile_data->get_flip_h(), tile_data->get_flip_v(), tile_data->get_transpose(), tile_data->get_modulate()); } -void TileDataIntegerEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) { - TileData *tile_data = _get_tile_data(p_tile_set, p_atlas_source_id, p_atlas_coords, p_alternative_tile); +void TileDataCollisionEditor::_set_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile, Variant p_value) { + TileData *tile_data = Object::cast_to(p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile)); ERR_FAIL_COND(!tile_data); - bool valid; - Variant value = tile_data->get(p_property, &valid); - if (!valid) { - return; + Array array = p_value; + tile_data->set_collision_polygons_count(physics_layer, array.size()); + for (int i = 0; i < array.size(); i++) { + Dictionary dict = array[i]; + tile_data->set_collision_polygon_points(physics_layer, i, dict["points"]); + tile_data->set_collision_polygon_one_way(physics_layer, i, dict["one_way"]); + tile_data->set_collision_polygon_one_way_margin(physics_layer, i, dict["one_way_margin"]); } - ERR_FAIL_COND(value.get_type() != Variant::INT); - Ref font = TileSetEditor::get_singleton()->get_theme_font("bold", "EditorFonts"); - int height = font->get_height(); - int width = 200; - p_canvas_item->draw_string(font, p_transform.get_origin() + Vector2i(-width / 2, height / 2), vformat("%d", value), HALIGN_CENTER, width, -1, Color(1, 1, 1), 1, Color(0, 0, 0, 1)); + polygon_editor->set_background(p_tile_set_atlas_source->get_texture(), p_tile_set_atlas_source->get_tile_texture_region(p_coords), p_tile_set_atlas_source->get_tile_effective_texture_offset(p_coords, p_alternative_tile), tile_data->get_flip_h(), tile_data->get_flip_v(), tile_data->get_transpose(), tile_data->get_modulate()); } -void TileDataFloatEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) { - TileData *tile_data = _get_tile_data(p_tile_set, p_atlas_source_id, p_atlas_coords, p_alternative_tile); - ERR_FAIL_COND(!tile_data); +Variant TileDataCollisionEditor::_get_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) { + TileData *tile_data = Object::cast_to(p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile)); + ERR_FAIL_COND_V(!tile_data, Variant()); - bool valid; - Variant value = tile_data->get(p_property, &valid); - if (!valid) { - return; + Array array; + for (int i = 0; i < tile_data->get_collision_polygons_count(physics_layer); i++) { + Dictionary dict; + dict["points"] = tile_data->get_collision_polygon_points(physics_layer, i); + dict["one_way"] = tile_data->is_collision_polygon_one_way(physics_layer, i); + dict["one_way_margin"] = tile_data->get_collision_polygon_one_way_margin(physics_layer, i); + array.push_back(dict); } - ERR_FAIL_COND(value.get_type() != Variant::FLOAT); + return array; +} - Ref font = TileSetEditor::get_singleton()->get_theme_font("bold", "EditorFonts"); - int height = font->get_height(); - int width = 200; - p_canvas_item->draw_string(font, p_transform.get_origin() + Vector2i(-width / 2, height / 2), vformat("%.2f", value), HALIGN_CENTER, width, -1, Color(1, 1, 1), 1, Color(0, 0, 0, 1)); +void TileDataCollisionEditor::_setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, Map p_previous_values, Variant p_new_value) { + Array new_array = p_new_value; + for (Map::Element *E = p_previous_values.front(); E; E = E->next()) { + Array old_array = E->get(); + + Vector2i coords = E->key().get_atlas_coords(); + undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygons_count", coords.x, coords.y, E->key().alternative_tile, physics_layer), old_array.size()); + for (int i = 0; i < old_array.size(); i++) { + Dictionary dict = old_array[i]; + undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygon_%d/points", coords.x, coords.y, E->key().alternative_tile, physics_layer, i), dict["points"]); + undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygon_%d/one_way", coords.x, coords.y, E->key().alternative_tile, physics_layer, i), dict["one_way"]); + undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygon_%d/one_way_margin", coords.x, coords.y, E->key().alternative_tile, physics_layer, i), dict["one_way_margin"]); + } + + undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygons_count", coords.x, coords.y, E->key().alternative_tile, physics_layer), new_array.size()); + for (int i = 0; i < new_array.size(); i++) { + Dictionary dict = new_array[i]; + undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygon_%d/points", coords.x, coords.y, E->key().alternative_tile, physics_layer, i), dict["points"]); + undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygon_%d/one_way", coords.x, coords.y, E->key().alternative_tile, physics_layer, i), dict["one_way"]); + undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygon_%d/one_way_margin", coords.x, coords.y, E->key().alternative_tile, physics_layer, i), dict["one_way_margin"]); + } + } } -void TileDataPositionEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) { - TileData *tile_data = _get_tile_data(p_tile_set, p_atlas_source_id, p_atlas_coords, p_alternative_tile); - ERR_FAIL_COND(!tile_data); +void TileDataCollisionEditor::_tile_set_changed() { + polygon_editor->set_tile_set(tile_set); + _polygons_changed(); +} - bool valid; - Variant value = tile_data->get(p_property, &valid); - if (!valid) { - return; +void TileDataCollisionEditor::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: + polygon_editor->set_polygons_color(get_tree()->get_debug_collisions_color()); + break; + default: + break; } - ERR_FAIL_COND(value.get_type() != Variant::VECTOR2I && value.get_type() != Variant::VECTOR2); +} - Ref position_icon = TileSetEditor::get_singleton()->get_theme_icon("EditorPosition", "EditorIcons"); - p_canvas_item->draw_texture(position_icon, p_transform.xform(Vector2(value)) - position_icon->get_size() / 2); +TileDataCollisionEditor::TileDataCollisionEditor() { + polygon_editor = memnew(GenericTilePolygonEditor); + polygon_editor->set_multiple_polygon_mode(true); + polygon_editor->connect("polygons_changed", callable_mp(this, &TileDataCollisionEditor::_polygons_changed)); + add_child(polygon_editor); + + _polygons_changed(); +} + +TileDataCollisionEditor::~TileDataCollisionEditor() { + memdelete(dummy_object); } -void TileDataYSortEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) { - TileData *tile_data = _get_tile_data(p_tile_set, p_atlas_source_id, p_atlas_coords, p_alternative_tile); +void TileDataCollisionEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected) { + TileData *tile_data = _get_tile_data(p_cell); ERR_FAIL_COND(!tile_data); - bool valid; - Variant value = tile_data->get(p_property, &valid); - if (!valid) { - return; + // Draw all shapes. + Vector color; + if (p_selected) { + Color grid_color = EditorSettings::get_singleton()->get("editors/tiles_editor/grid_color"); + Color selection_color = Color().from_hsv(Math::fposmod(grid_color.get_h() + 0.5, 1.0), grid_color.get_s(), grid_color.get_v(), 1.0); + selection_color.a = 0.7; + color.push_back(selection_color); + } else { + Color debug_collision_color = p_canvas_item->get_tree()->get_debug_collisions_color(); + color.push_back(debug_collision_color); } - ERR_FAIL_COND(value.get_type() != Variant::INT); - Ref position_icon = TileSetEditor::get_singleton()->get_theme_icon("EditorPosition", "EditorIcons"); - p_canvas_item->draw_texture(position_icon, p_transform.xform(Vector2(0, value)) - position_icon->get_size() / 2); + RenderingServer::get_singleton()->canvas_item_add_set_transform(p_canvas_item->get_canvas_item(), p_transform); + for (int i = 0; i < tile_data->get_collision_polygons_count(physics_layer); i++) { + Vector polygon = tile_data->get_collision_polygon_points(physics_layer, i); + if (polygon.size() >= 3) { + p_canvas_item->draw_polygon(polygon, color); + } + } + RenderingServer::get_singleton()->canvas_item_add_set_transform(p_canvas_item->get_canvas_item(), Transform2D()); } -void TileDataOcclusionShapeEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) { - TileData *tile_data = _get_tile_data(p_tile_set, p_atlas_source_id, p_atlas_coords, p_alternative_tile); - ERR_FAIL_COND(!tile_data); +void TileDataTerrainsEditor::_update_terrain_selector() { + ERR_FAIL_COND(!tile_set.is_valid()); - Vector components = String(p_property).split("/", true); - if (components[0].begins_with("occlusion_layer_") && components[0].trim_prefix("occlusion_layer_").is_valid_int()) { - int occlusion_layer = components[0].trim_prefix("occlusion_layer_").to_int(); - if (occlusion_layer >= 0 && occlusion_layer < p_tile_set->get_occlusion_layers_count()) { - // Draw all shapes. - Vector debug_occlusion_color; - debug_occlusion_color.push_back(Color(0.5, 0, 0, 0.6)); + // Update the terrain set selector. + Vector options; + options.push_back(String(TTR("No terrains")) + String(":-1")); + for (int i = 0; i < tile_set->get_terrain_sets_count(); i++) { + options.push_back(vformat("Terrain Set %d", i)); + } + terrain_set_property_editor->setup(options); + terrain_set_property_editor->update_property(); - RenderingServer::get_singleton()->canvas_item_add_set_transform(p_canvas_item->get_canvas_item(), p_transform); - Ref occluder = tile_data->get_occluder(occlusion_layer); - if (occluder.is_valid() && occluder->get_polygon().size() >= 3) { - p_canvas_item->draw_polygon(Variant(occluder->get_polygon()), debug_occlusion_color); + // Update the terrain selector. + int terrain_set = int(dummy_object->get("terrain_set")); + if (terrain_set == -1) { + terrain_property_editor->hide(); + } else { + options.clear(); + Vector>> icons = tile_set->generate_terrains_icons(Size2(16, 16) * EDSCALE); + options.push_back(String(TTR("No terrain")) + String(":-1")); + for (int i = 0; i < tile_set->get_terrains_count(terrain_set); i++) { + String name = tile_set->get_terrain_name(terrain_set, i); + if (name.is_empty()) { + options.push_back(vformat("Terrain %d", i)); + } else { + options.push_back(name); } - RenderingServer::get_singleton()->canvas_item_add_set_transform(p_canvas_item->get_canvas_item(), Transform2D()); } + terrain_property_editor->setup(options); + terrain_property_editor->update_property(); + + // Kind of a hack to set icons. + // We could provide a way to modify that in the EditorProperty. + OptionButton *option_button = Object::cast_to(terrain_property_editor->get_child(0)); + for (int terrain = 0; terrain < tile_set->get_terrains_count(terrain_set); terrain++) { + option_button->set_item_icon(terrain + 1, icons[terrain_set][terrain]); + } + terrain_property_editor->show(); } } -void TileDataCollisionShapeEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) { - TileData *tile_data = _get_tile_data(p_tile_set, p_atlas_source_id, p_atlas_coords, p_alternative_tile); - ERR_FAIL_COND(!tile_data); +void TileDataTerrainsEditor::_property_value_changed(StringName p_property, Variant p_value, StringName p_field) { + Variant old_value = dummy_object->get(p_property); + dummy_object->set(p_property, p_value); + if (p_property == "terrain_set") { + if (p_value != old_value) { + dummy_object->set("terrain", -1); + } + _update_terrain_selector(); + } + emit_signal("needs_redraw"); +} + +void TileDataTerrainsEditor::_tile_set_changed() { + ERR_FAIL_COND(!tile_set.is_valid()); + + // Fix if wrong values are selected. + if (int(dummy_object->get("terrain_set")) > tile_set->get_terrain_sets_count()) { + dummy_object->set("terrain_set", -1); + } + int terrain_set = int(dummy_object->get("terrain")); + if (terrain_set >= 0) { + if (int(dummy_object->get("terrain")) > tile_set->get_terrains_count(terrain_set)) { + dummy_object->set("terrain", -1); + } + } + + _update_terrain_selector(); +} + +void TileDataTerrainsEditor::forward_draw_over_atlas(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_set_atlas_source, CanvasItem *p_canvas_item, Transform2D p_transform) { + ERR_FAIL_COND(!tile_set.is_valid()); + + // Draw the hovered terrain bit, or the whole tile if it has the wrong terrain set. + Vector2i hovered_coords = TileSetSource::INVALID_ATLAS_COORDS; + if (drag_type == DRAG_TYPE_NONE) { + Vector2i mouse_pos = p_transform.affine_inverse().xform(p_canvas_item->get_local_mouse_position()); + hovered_coords = p_tile_atlas_view->get_atlas_tile_coords_at_pos(mouse_pos); + hovered_coords = p_tile_set_atlas_source->get_tile_at_coords(hovered_coords); + if (hovered_coords != TileSetSource::INVALID_ATLAS_COORDS) { + TileData *tile_data = Object::cast_to(p_tile_set_atlas_source->get_tile_data(hovered_coords, 0)); + int terrain_set = tile_data->get_terrain_set(); + Rect2i texture_region = p_tile_set_atlas_source->get_tile_texture_region(hovered_coords); + Vector2i position = (texture_region.position + texture_region.get_end()) / 2 + p_tile_set_atlas_source->get_tile_effective_texture_offset(hovered_coords, 0); + + if (terrain_set >= 0 && terrain_set == int(dummy_object->get("terrain_set"))) { + // Draw hovered bit. + Transform2D xform; + xform.set_origin(position); + + Vector color; + color.push_back(Color(1.0, 1.0, 1.0, 0.5)); + + for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { + TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); + if (tile_set->is_valid_peering_bit_terrain(terrain_set, bit)) { + Vector polygon = tile_set->get_terrain_bit_polygon(terrain_set, bit); + if (Geometry2D::is_point_in_polygon(xform.affine_inverse().xform(mouse_pos), polygon)) { + p_canvas_item->draw_set_transform_matrix(p_transform * xform); + p_canvas_item->draw_polygon(polygon, color); + } + } + } + } else { + // Draw hovered tile. + Vector2i tile_size = tile_set->get_tile_size(); + Rect2i rect = p_transform.xform(Rect2i(position - tile_size / 2, tile_size)); + tile_set->draw_tile_shape(p_canvas_item, rect, Color(1.0, 1.0, 1.0, 0.5), true); + } + } + } + + // Dim terrains with wrong terrain set. + Ref font = TileSetEditor::get_singleton()->get_theme_font("bold", "EditorFonts"); + for (int i = 0; i < p_tile_set_atlas_source->get_tiles_count(); i++) { + Vector2i coords = p_tile_set_atlas_source->get_tile_id(i); + if (coords != hovered_coords) { + TileData *tile_data = Object::cast_to(p_tile_set_atlas_source->get_tile_data(coords, 0)); + if (tile_data->get_terrain_set() != int(dummy_object->get("terrain_set"))) { + // Dimming + p_canvas_item->draw_set_transform_matrix(p_transform); + Rect2i rect = p_tile_set_atlas_source->get_tile_texture_region(coords); + p_canvas_item->draw_rect(rect, Color(0.0, 0.0, 0.0, 0.3)); + + // Text + p_canvas_item->draw_set_transform_matrix(Transform2D()); + Rect2i texture_region = p_tile_set_atlas_source->get_tile_texture_region(coords); + Vector2i position = (texture_region.position + texture_region.get_end()) / 2 + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0); + + Color color = Color(1, 1, 1); + String text; + if (tile_data->get_terrain_set() >= 0) { + text = vformat("%d", tile_data->get_terrain_set()); + } else { + text = "-"; + } + Vector2 string_size = font->get_string_size(text); + p_canvas_item->draw_string(font, p_transform.xform(position) + Vector2i(-string_size.x / 2, string_size.y / 2), text, HALIGN_CENTER, string_size.x, -1, color, 1, Color(0, 0, 0, 1)); + } + } + } + p_canvas_item->draw_set_transform_matrix(Transform2D()); + + if (drag_type == DRAG_TYPE_PAINT_TERRAIN_SET_RECT) { + // Draw selection rectangle. + Color grid_color = EditorSettings::get_singleton()->get("editors/tiles_editor/grid_color"); + Color selection_color = Color().from_hsv(Math::fposmod(grid_color.get_h() + 0.5, 1.0), grid_color.get_s(), grid_color.get_v(), 1.0); + + p_canvas_item->draw_set_transform_matrix(p_transform); + + Rect2i rect; + rect.set_position(p_tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_pos)); + rect.set_end(p_tile_atlas_view->get_atlas_tile_coords_at_pos(p_transform.affine_inverse().xform(p_canvas_item->get_local_mouse_position()))); + rect = rect.abs(); + + Set edited; + for (int x = rect.get_position().x; x <= rect.get_end().x; x++) { + for (int y = rect.get_position().y; y <= rect.get_end().y; y++) { + Vector2i coords = Vector2i(x, y); + coords = p_tile_set_atlas_source->get_tile_at_coords(coords); + if (coords != TileSetSource::INVALID_ATLAS_COORDS) { + TileMapCell cell; + cell.source_id = 0; + cell.set_atlas_coords(coords); + cell.alternative_tile = 0; + edited.insert(cell); + } + } + } + + for (Set::Element *E = edited.front(); E; E = E->next()) { + Vector2i coords = E->get().get_atlas_coords(); + p_canvas_item->draw_rect(p_tile_set_atlas_source->get_tile_texture_region(coords), selection_color, false); + } + p_canvas_item->draw_set_transform_matrix(Transform2D()); + } else if (drag_type == DRAG_TYPE_PAINT_TERRAIN_BITS_RECT) { + // Highlight selected peering bits. + Dictionary painted = Dictionary(drag_painted_value); + int terrain_set = int(painted["terrain_set"]); + + Rect2i rect; + rect.set_position(p_tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_pos)); + rect.set_end(p_tile_atlas_view->get_atlas_tile_coords_at_pos(p_transform.affine_inverse().xform(p_canvas_item->get_local_mouse_position()))); + rect = rect.abs(); + + Set edited; + for (int x = rect.get_position().x; x <= rect.get_end().x; x++) { + for (int y = rect.get_position().y; y <= rect.get_end().y; y++) { + Vector2i coords = Vector2i(x, y); + coords = p_tile_set_atlas_source->get_tile_at_coords(coords); + if (coords != TileSetSource::INVALID_ATLAS_COORDS) { + TileData *tile_data = Object::cast_to(p_tile_set_atlas_source->get_tile_data(coords, 0)); + if (tile_data->get_terrain_set() == terrain_set) { + TileMapCell cell; + cell.source_id = 0; + cell.set_atlas_coords(coords); + cell.alternative_tile = 0; + edited.insert(cell); + } + } + } + } + + Vector2 end = p_transform.affine_inverse().xform(p_canvas_item->get_local_mouse_position()); + Vector mouse_pos_rect_polygon; + mouse_pos_rect_polygon.push_back(drag_start_pos); + mouse_pos_rect_polygon.push_back(Vector2(end.x, drag_start_pos.y)); + mouse_pos_rect_polygon.push_back(end); + mouse_pos_rect_polygon.push_back(Vector2(drag_start_pos.x, end.y)); + + Vector color; + color.push_back(Color(1.0, 1.0, 1.0, 0.5)); + + p_canvas_item->draw_set_transform_matrix(p_transform); + + for (Set::Element *E = edited.front(); E; E = E->next()) { + Vector2i coords = E->get().get_atlas_coords(); + + Rect2i texture_region = p_tile_set_atlas_source->get_tile_texture_region(coords); + Vector2i position = (texture_region.position + texture_region.get_end()) / 2 + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0); - Vector components = String(p_property).split("/", true); - if (components[0].begins_with("physics_layer_") && components[0].trim_prefix("physics_layer_").is_valid_int()) { - int physics_layer = components[0].trim_prefix("physics_layer_").to_int(); - if (physics_layer >= 0 && physics_layer < p_tile_set->get_physics_layers_count()) { - // Draw all shapes. - Color debug_collision_color = p_canvas_item->get_tree()->get_debug_collisions_color(); - RenderingServer::get_singleton()->canvas_item_add_set_transform(p_canvas_item->get_canvas_item(), p_transform); - for (int i = 0; i < tile_data->get_collision_shapes_count(physics_layer); i++) { - Ref shape = tile_data->get_collision_shape_shape(physics_layer, i); - if (shape.is_valid()) { - shape->draw(p_canvas_item->get_canvas_item(), debug_collision_color); + for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { + TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); + if (tile_set->is_valid_peering_bit_terrain(terrain_set, bit)) { + Vector polygon = tile_set->get_terrain_bit_polygon(terrain_set, bit); + for (int j = 0; j < polygon.size(); j++) { + polygon.write[j] += position; + } + if (!Geometry2D::intersect_polygons(polygon, mouse_pos_rect_polygon).is_empty()) { + // Draw bit. + p_canvas_item->draw_polygon(polygon, color); + } } } - RenderingServer::get_singleton()->canvas_item_add_set_transform(p_canvas_item->get_canvas_item(), Transform2D()); } + + p_canvas_item->draw_set_transform_matrix(Transform2D()); } } -void TileDataTerrainsEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) { - TileData *tile_data = _get_tile_data(p_tile_set, p_atlas_source_id, p_atlas_coords, p_alternative_tile); - ERR_FAIL_COND(!tile_data); +void TileDataTerrainsEditor::forward_draw_over_alternatives(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_set_atlas_source, CanvasItem *p_canvas_item, Transform2D p_transform) { + ERR_FAIL_COND(!tile_set.is_valid()); + + // Draw the hovered terrain bit, or the whole tile if it has the wrong terrain set. + Vector2i hovered_coords = TileSetSource::INVALID_ATLAS_COORDS; + int hovered_alternative = TileSetSource::INVALID_TILE_ALTERNATIVE; + if (drag_type == DRAG_TYPE_NONE) { + Vector2i mouse_pos = p_transform.affine_inverse().xform(p_canvas_item->get_local_mouse_position()); + Vector3i hovered = p_tile_atlas_view->get_alternative_tile_at_pos(mouse_pos); + hovered_coords = Vector2i(hovered.x, hovered.y); + hovered_alternative = hovered.z; + if (hovered_coords != TileSetSource::INVALID_ATLAS_COORDS) { + TileData *tile_data = Object::cast_to(p_tile_set_atlas_source->get_tile_data(hovered_coords, hovered_alternative)); + int terrain_set = tile_data->get_terrain_set(); + Rect2i texture_region = p_tile_atlas_view->get_alternative_tile_rect(hovered_coords, hovered_alternative); + Vector2i position = (texture_region.position + texture_region.get_end()) / 2 + p_tile_set_atlas_source->get_tile_effective_texture_offset(hovered_coords, hovered_alternative); + + if (terrain_set == int(dummy_object->get("terrain_set"))) { + // Draw hovered bit. + Transform2D xform; + xform.set_origin(position); + + Vector color; + color.push_back(Color(1.0, 1.0, 1.0, 0.5)); + + for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { + TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); + if (tile_set->is_valid_peering_bit_terrain(terrain_set, bit)) { + Vector polygon = tile_set->get_terrain_bit_polygon(terrain_set, bit); + if (Geometry2D::is_point_in_polygon(xform.affine_inverse().xform(mouse_pos), polygon)) { + p_canvas_item->draw_set_transform_matrix(p_transform * xform); + p_canvas_item->draw_polygon(polygon, color); + } + } + } + } else { + // Draw hovered tile. + Vector2i tile_size = tile_set->get_tile_size(); + Rect2i rect = p_transform.xform(Rect2i(position - tile_size / 2, tile_size)); + tile_set->draw_tile_shape(p_canvas_item, rect, Color(1.0, 1.0, 1.0, 0.5), true); + } + } + } + + // Dim terrains with wrong terrain set. + Ref font = TileSetEditor::get_singleton()->get_theme_font("bold", "EditorFonts"); + for (int i = 0; i < p_tile_set_atlas_source->get_tiles_count(); i++) { + Vector2i coords = p_tile_set_atlas_source->get_tile_id(i); + for (int j = 1; j < p_tile_set_atlas_source->get_alternative_tiles_count(coords); j++) { + int alternative_tile = p_tile_set_atlas_source->get_alternative_tile_id(coords, j); + if (coords != hovered_coords || alternative_tile != hovered_alternative) { + TileData *tile_data = Object::cast_to(p_tile_set_atlas_source->get_tile_data(coords, alternative_tile)); + if (tile_data->get_terrain_set() != int(dummy_object->get("terrain_set"))) { + // Dimming + p_canvas_item->draw_set_transform_matrix(p_transform); + Rect2i rect = p_tile_atlas_view->get_alternative_tile_rect(coords, alternative_tile); + p_canvas_item->draw_rect(rect, Color(0.0, 0.0, 0.0, 0.3)); - Vector components = String(p_property).split("/", true); - if (components[0] == "terrain_mode" || components[0] == "terrain" || components[0] == "terrains_peering_bit") { - TileSetPluginAtlasTerrain::draw_terrains(p_canvas_item, p_transform, p_tile_set, tile_data); + // Text + p_canvas_item->draw_set_transform_matrix(Transform2D()); + Rect2i texture_region = p_tile_atlas_view->get_alternative_tile_rect(coords, alternative_tile); + Vector2i position = (texture_region.position + texture_region.get_end()) / 2 + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0); + + Color color = Color(1, 1, 1); + String text; + if (tile_data->get_terrain_set() >= 0) { + text = vformat("%d", tile_data->get_terrain_set()); + } else { + text = "-"; + } + Vector2 string_size = font->get_string_size(text); + p_canvas_item->draw_string(font, p_transform.xform(position) + Vector2i(-string_size.x / 2, string_size.y / 2), text, HALIGN_CENTER, string_size.x, -1, color, 1, Color(0, 0, 0, 1)); + } + } + } } + + p_canvas_item->draw_set_transform_matrix(Transform2D()); } -void TileDataNavigationPolygonEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) { - TileData *tile_data = _get_tile_data(p_tile_set, p_atlas_source_id, p_atlas_coords, p_alternative_tile); - ERR_FAIL_COND(!tile_data); +void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_set_atlas_source, const Ref &p_event) { + Ref mm = p_event; + if (mm.is_valid()) { + if (drag_type == DRAG_TYPE_PAINT_TERRAIN_SET) { + Vector line = Geometry2D::bresenham_line(p_tile_atlas_view->get_atlas_tile_coords_at_pos(drag_last_pos), p_tile_atlas_view->get_atlas_tile_coords_at_pos(mm->get_position())); + for (int i = 0; i < line.size(); i++) { + Vector2i coords = p_tile_set_atlas_source->get_tile_at_coords(line[i]); + if (coords != TileSetSource::INVALID_ATLAS_COORDS) { + int terrain_set = drag_painted_value; + TileMapCell cell; + cell.source_id = 0; + cell.set_atlas_coords(coords); + cell.alternative_tile = 0; - Vector components = String(p_property).split("/", true); - if (components[0].begins_with("navigation_layer_") && components[0].trim_prefix("navigation_layer_").is_valid_int()) { - int navigation_layer = components[0].trim_prefix("navigation_layer_").to_int(); - if (navigation_layer >= 0 && navigation_layer < p_tile_set->get_navigation_layers_count()) { - // Draw all shapes. - RenderingServer::get_singleton()->canvas_item_add_set_transform(p_canvas_item->get_canvas_item(), p_transform); + // Save the old terrain_set and terrains bits. + TileData *tile_data = Object::cast_to(p_tile_set_atlas_source->get_tile_data(coords, 0)); + if (!drag_modified.has(cell)) { + Dictionary dict; + dict["terrain_set"] = tile_data->get_terrain_set(); + Array array; + for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) { + TileSet::CellNeighbor bit = TileSet::CellNeighbor(j); + array.push_back(tile_data->is_valid_peering_bit_terrain(bit) ? tile_data->get_peering_bit_terrain(bit) : -1); + } + dict["terrain_peering_bits"] = array; + drag_modified[cell] = dict; + } - Ref navigation_polygon = tile_data->get_navigation_polygon(navigation_layer); - if (navigation_polygon.is_valid()) { - Vector verts = navigation_polygon->get_vertices(); - if (verts.size() < 3) { - return; + // Set the terrain_set. + tile_data->set_terrain_set(terrain_set); } + } + drag_last_pos = mm->get_position(); + } else if (drag_type == DRAG_TYPE_PAINT_TERRAIN_BITS) { + int terrain_set = Dictionary(drag_painted_value)["terrain_set"]; + int terrain = Dictionary(drag_painted_value)["terrain"]; + Vector line = Geometry2D::bresenham_line(p_tile_atlas_view->get_atlas_tile_coords_at_pos(drag_last_pos), p_tile_atlas_view->get_atlas_tile_coords_at_pos(mm->get_position())); + for (int i = 0; i < line.size(); i++) { + Vector2i coords = p_tile_set_atlas_source->get_tile_at_coords(line[i]); + if (coords != TileSetSource::INVALID_ATLAS_COORDS) { + TileMapCell cell; + cell.source_id = 0; + cell.set_atlas_coords(coords); + cell.alternative_tile = 0; - Color color = p_canvas_item->get_tree()->get_debug_navigation_color(); + TileData *tile_data = Object::cast_to(p_tile_set_atlas_source->get_tile_data(coords, 0)); + if (tile_data->get_terrain_set() == terrain_set) { + // Save the old terrain_set and terrains bits. + if (!drag_modified.has(cell)) { + Dictionary dict; + dict["terrain_set"] = tile_data->get_terrain_set(); + Array array; + for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) { + TileSet::CellNeighbor bit = TileSet::CellNeighbor(j); + array.push_back(tile_data->is_valid_peering_bit_terrain(bit) ? tile_data->get_peering_bit_terrain(bit) : -1); + } + dict["terrain_peering_bits"] = array; + drag_modified[cell] = dict; + } - RandomPCG rand; - for (int i = 0; i < navigation_polygon->get_polygon_count(); i++) { - // An array of vertices for this polygon. - Vector polygon = navigation_polygon->get_polygon(i); - Vector vertices; - vertices.resize(polygon.size()); - for (int j = 0; j < polygon.size(); j++) { - ERR_FAIL_INDEX(polygon[j], verts.size()); - vertices.write[j] = verts[polygon[j]]; + // Set the terrains bits. + Rect2i texture_region = p_tile_set_atlas_source->get_tile_texture_region(coords); + Vector2i position = (texture_region.position + texture_region.get_end()) / 2 + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0); + for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) { + TileSet::CellNeighbor bit = TileSet::CellNeighbor(j); + if (tile_data->is_valid_peering_bit_terrain(bit)) { + Vector polygon = tile_set->get_terrain_bit_polygon(tile_data->get_terrain_set(), bit); + if (Geometry2D::is_segment_intersecting_polygon(mm->get_position() - position, drag_last_pos - position, polygon)) { + tile_data->set_peering_bit_terrain(bit, terrain); + } + } + } + } + } + } + drag_last_pos = mm->get_position(); + } + } + + Ref mb = p_event; + if (mb.is_valid()) { + if (mb->get_button_index() == MOUSE_BUTTON_LEFT) { + if (mb->is_pressed()) { + if (picker_button->is_pressed()) { + Vector2i coords = p_tile_atlas_view->get_atlas_tile_coords_at_pos(mb->get_position()); + coords = p_tile_set_atlas_source->get_tile_at_coords(coords); + if (coords != TileSetSource::INVALID_ATLAS_COORDS) { + TileData *tile_data = Object::cast_to(p_tile_set_atlas_source->get_tile_data(coords, 0)); + int terrain_set = tile_data->get_terrain_set(); + Rect2i texture_region = p_tile_set_atlas_source->get_tile_texture_region(coords); + Vector2i position = (texture_region.position + texture_region.get_end()) / 2 + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0); + dummy_object->set("terrain_set", terrain_set); + dummy_object->set("terrain", -1); + for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { + TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); + if (tile_set->is_valid_peering_bit_terrain(terrain_set, bit)) { + Vector polygon = tile_set->get_terrain_bit_polygon(terrain_set, bit); + if (Geometry2D::is_point_in_polygon(mb->get_position() - position, polygon)) { + dummy_object->set("terrain", tile_data->get_peering_bit_terrain(bit)); + } + } + } + terrain_set_property_editor->update_property(); + _update_terrain_selector(); + picker_button->set_pressed(false); + } + } else { + Vector2i coords = p_tile_atlas_view->get_atlas_tile_coords_at_pos(mb->get_position()); + coords = p_tile_set_atlas_source->get_tile_at_coords(coords); + TileData *tile_data = nullptr; + if (coords != TileSetAtlasSource::INVALID_ATLAS_COORDS) { + tile_data = Object::cast_to(p_tile_set_atlas_source->get_tile_data(coords, 0)); + } + int terrain_set = int(dummy_object->get("terrain_set")); + int terrain = int(dummy_object->get("terrain")); + if (terrain_set == -1 || !tile_data || tile_data->get_terrain_set() != terrain_set) { + if (mb->is_ctrl_pressed()) { + // Paint terrain set with rect. + drag_type = DRAG_TYPE_PAINT_TERRAIN_SET_RECT; + drag_modified.clear(); + drag_painted_value = terrain_set; + drag_start_pos = mb->get_position(); + } else { + // Paint terrain set. + drag_type = DRAG_TYPE_PAINT_TERRAIN_SET; + drag_modified.clear(); + drag_painted_value = terrain_set; + + if (coords != TileSetSource::INVALID_ATLAS_COORDS) { + TileMapCell cell; + cell.source_id = 0; + cell.set_atlas_coords(coords); + cell.alternative_tile = 0; + + // Save the old terrain_set and terrains bits. + Dictionary dict; + dict["terrain_set"] = tile_data->get_terrain_set(); + Array array; + for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { + TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); + array.push_back(tile_data->is_valid_peering_bit_terrain(bit) ? tile_data->get_peering_bit_terrain(bit) : -1); + } + dict["terrain_peering_bits"] = array; + drag_modified[cell] = dict; + + // Set the terrain_set. + tile_data->set_terrain_set(terrain_set); + } + drag_last_pos = mb->get_position(); + } + } else if (tile_data && tile_data->get_terrain_set() == terrain_set) { + if (mb->is_ctrl_pressed()) { + // Paint terrain set with rect. + drag_type = DRAG_TYPE_PAINT_TERRAIN_BITS_RECT; + drag_modified.clear(); + Dictionary painted_dict; + painted_dict["terrain_set"] = terrain_set; + painted_dict["terrain"] = terrain; + drag_painted_value = painted_dict; + drag_start_pos = mb->get_position(); + } else { + // Paint terrain bits. + drag_type = DRAG_TYPE_PAINT_TERRAIN_BITS; + drag_modified.clear(); + Dictionary painted_dict; + painted_dict["terrain_set"] = terrain_set; + painted_dict["terrain"] = terrain; + drag_painted_value = painted_dict; + + if (coords != TileSetSource::INVALID_ATLAS_COORDS) { + TileMapCell cell; + cell.source_id = 0; + cell.set_atlas_coords(coords); + cell.alternative_tile = 0; + + // Save the old terrain_set and terrains bits. + Dictionary dict; + dict["terrain_set"] = tile_data->get_terrain_set(); + Array array; + for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { + TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); + array.push_back(tile_data->is_valid_peering_bit_terrain(bit) ? tile_data->get_peering_bit_terrain(bit) : -1); + } + dict["terrain_peering_bits"] = array; + drag_modified[cell] = dict; + + // Set the terrain bit. + Rect2i texture_region = p_tile_set_atlas_source->get_tile_texture_region(coords); + Vector2i position = (texture_region.position + texture_region.get_end()) / 2 + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0); + + for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { + TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); + if (tile_set->is_valid_peering_bit_terrain(terrain_set, bit)) { + Vector polygon = tile_set->get_terrain_bit_polygon(terrain_set, bit); + if (Geometry2D::is_point_in_polygon(mb->get_position() - position, polygon)) { + tile_data->set_peering_bit_terrain(bit, terrain); + } + } + } + } + drag_last_pos = mb->get_position(); + } + } + } + } else { + if (drag_type == DRAG_TYPE_PAINT_TERRAIN_SET_RECT) { + Rect2i rect; + rect.set_position(p_tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_pos)); + rect.set_end(p_tile_atlas_view->get_atlas_tile_coords_at_pos(mb->get_position())); + rect = rect.abs(); + + Set edited; + for (int x = rect.get_position().x; x <= rect.get_end().x; x++) { + for (int y = rect.get_position().y; y <= rect.get_end().y; y++) { + Vector2i coords = Vector2i(x, y); + coords = p_tile_set_atlas_source->get_tile_at_coords(coords); + if (coords != TileSetSource::INVALID_ATLAS_COORDS) { + TileMapCell cell; + cell.source_id = 0; + cell.set_atlas_coords(coords); + cell.alternative_tile = 0; + edited.insert(cell); + } + } + } + undo_redo->create_action(TTR("Painting Terrain Set")); + for (Set::Element *E = edited.front(); E; E = E->next()) { + Vector2i coords = E->get().get_atlas_coords(); + TileData *tile_data = Object::cast_to(p_tile_set_atlas_source->get_tile_data(coords, 0)); + undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain_set", coords.x, coords.y, E->get().alternative_tile), tile_data->get_terrain_set()); + undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain_set", coords.x, coords.y, E->get().alternative_tile), drag_painted_value); + for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { + TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); + if (tile_data->is_valid_peering_bit_terrain(bit)) { + undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E->get().alternative_tile), tile_data->get_peering_bit_terrain(bit)); + } + } + } + undo_redo->commit_action(true); + drag_type = DRAG_TYPE_NONE; + } else if (drag_type == DRAG_TYPE_PAINT_TERRAIN_SET) { + undo_redo->create_action(TTR("Painting Terrain Set")); + for (Map::Element *E = drag_modified.front(); E; E = E->next()) { + Dictionary dict = E->get(); + Vector2i coords = E->key().get_atlas_coords(); + undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain_set", coords.x, coords.y, E->key().alternative_tile), drag_painted_value); + undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain_set", coords.x, coords.y, E->key().alternative_tile), dict["terrain_set"]); + Array array = dict["terrain_peering_bits"]; + for (int i = 0; i < array.size(); i++) { + TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); + if (tile_set->is_valid_peering_bit_terrain(dict["terrain_set"], bit)) { + undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E->key().alternative_tile), array[i]); + } + } + } + undo_redo->commit_action(false); + drag_type = DRAG_TYPE_NONE; + } else if (drag_type == DRAG_TYPE_PAINT_TERRAIN_BITS) { + Dictionary painted = Dictionary(drag_painted_value); + int terrain_set = int(painted["terrain_set"]); + int terrain = int(painted["terrain"]); + undo_redo->create_action(TTR("Painting Terrain")); + for (Map::Element *E = drag_modified.front(); E; E = E->next()) { + Dictionary dict = E->get(); + Vector2i coords = E->key().get_atlas_coords(); + Array array = dict["terrain_peering_bits"]; + for (int i = 0; i < array.size(); i++) { + TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); + if (tile_set->is_valid_peering_bit_terrain(terrain_set, bit)) { + undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E->key().alternative_tile), terrain); + } + if (tile_set->is_valid_peering_bit_terrain(dict["terrain_set"], bit)) { + undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E->key().alternative_tile), array[i]); + } + } + } + undo_redo->commit_action(false); + drag_type = DRAG_TYPE_NONE; + } else if (drag_type == DRAG_TYPE_PAINT_TERRAIN_BITS_RECT) { + Dictionary painted = Dictionary(drag_painted_value); + int terrain_set = int(painted["terrain_set"]); + int terrain = int(painted["terrain"]); + + Rect2i rect; + rect.set_position(p_tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_pos)); + rect.set_end(p_tile_atlas_view->get_atlas_tile_coords_at_pos(mb->get_position())); + rect = rect.abs(); + + Set edited; + for (int x = rect.get_position().x; x <= rect.get_end().x; x++) { + for (int y = rect.get_position().y; y <= rect.get_end().y; y++) { + Vector2i coords = Vector2i(x, y); + coords = p_tile_set_atlas_source->get_tile_at_coords(coords); + if (coords != TileSetSource::INVALID_ATLAS_COORDS) { + TileData *tile_data = Object::cast_to(p_tile_set_atlas_source->get_tile_data(coords, 0)); + if (tile_data->get_terrain_set() == terrain_set) { + TileMapCell cell; + cell.source_id = 0; + cell.set_atlas_coords(coords); + cell.alternative_tile = 0; + edited.insert(cell); + } + } + } + } + + Vector mouse_pos_rect_polygon; + mouse_pos_rect_polygon.push_back(drag_start_pos); + mouse_pos_rect_polygon.push_back(Vector2(mb->get_position().x, drag_start_pos.y)); + mouse_pos_rect_polygon.push_back(mb->get_position()); + mouse_pos_rect_polygon.push_back(Vector2(drag_start_pos.x, mb->get_position().y)); + + undo_redo->create_action(TTR("Painting Terrain")); + for (Set::Element *E = edited.front(); E; E = E->next()) { + Vector2i coords = E->get().get_atlas_coords(); + TileData *tile_data = Object::cast_to(p_tile_set_atlas_source->get_tile_data(coords, 0)); + + for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { + TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); + if (tile_set->is_valid_peering_bit_terrain(terrain_set, bit)) { + Rect2i texture_region = p_tile_set_atlas_source->get_tile_texture_region(coords); + Vector2i position = (texture_region.position + texture_region.get_end()) / 2 + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0); + + Vector polygon = tile_set->get_terrain_bit_polygon(terrain_set, bit); + for (int j = 0; j < polygon.size(); j++) { + polygon.write[j] += position; + } + if (!Geometry2D::intersect_polygons(polygon, mouse_pos_rect_polygon).is_empty()) { + // Draw bit. + undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E->get().alternative_tile), terrain); + undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E->get().alternative_tile), tile_data->get_peering_bit_terrain(bit)); + } + } + } + } + undo_redo->commit_action(true); + drag_type = DRAG_TYPE_NONE; + } + } + } + } +} + +void TileDataTerrainsEditor::forward_painting_alternatives_gui_input(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_set_atlas_source, const Ref &p_event) { + Ref mm = p_event; + if (mm.is_valid()) { + if (drag_type == DRAG_TYPE_PAINT_TERRAIN_SET) { + Vector3i tile = p_tile_atlas_view->get_alternative_tile_at_pos(mm->get_position()); + Vector2i coords = Vector2i(tile.x, tile.y); + int alternative_tile = tile.z; + + if (coords != TileSetSource::INVALID_ATLAS_COORDS) { + TileMapCell cell; + cell.source_id = 0; + cell.set_atlas_coords(coords); + cell.alternative_tile = alternative_tile; + TileData *tile_data = Object::cast_to(p_tile_set_atlas_source->get_tile_data(coords, alternative_tile)); + if (!drag_modified.has(cell)) { + Dictionary dict; + dict["terrain_set"] = tile_data->get_terrain_set(); + Array array; + for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) { + TileSet::CellNeighbor bit = TileSet::CellNeighbor(j); + array.push_back(tile_data->is_valid_peering_bit_terrain(bit) ? tile_data->get_peering_bit_terrain(bit) : -1); + } + dict["terrain_peering_bits"] = array; + drag_modified[cell] = dict; + } + tile_data->set_terrain_set(drag_painted_value); + } + + drag_last_pos = mm->get_position(); + } else if (drag_type == DRAG_TYPE_PAINT_TERRAIN_BITS) { + Dictionary painted = Dictionary(drag_painted_value); + int terrain_set = int(painted["terrain_set"]); + int terrain = int(painted["terrain"]); + + Vector3i tile = p_tile_atlas_view->get_alternative_tile_at_pos(mm->get_position()); + Vector2i coords = Vector2i(tile.x, tile.y); + int alternative_tile = tile.z; + + if (coords != TileSetSource::INVALID_ATLAS_COORDS) { + TileMapCell cell; + cell.source_id = 0; + cell.set_atlas_coords(coords); + cell.alternative_tile = alternative_tile; + + // Save the old terrain_set and terrains bits. + TileData *tile_data = Object::cast_to(p_tile_set_atlas_source->get_tile_data(coords, alternative_tile)); + if (tile_data->get_terrain_set() == terrain_set) { + if (!drag_modified.has(cell)) { + Dictionary dict; + dict["terrain_set"] = tile_data->get_terrain_set(); + Array array; + for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) { + TileSet::CellNeighbor bit = TileSet::CellNeighbor(j); + array.push_back(tile_data->is_valid_peering_bit_terrain(bit) ? tile_data->get_peering_bit_terrain(bit) : -1); + } + dict["terrain_peering_bits"] = array; + drag_modified[cell] = dict; + } + + // Set the terrains bits. + Rect2i texture_region = p_tile_atlas_view->get_alternative_tile_rect(coords, alternative_tile); + Vector2i position = (texture_region.position + texture_region.get_end()) / 2 + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, alternative_tile); + for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) { + TileSet::CellNeighbor bit = TileSet::CellNeighbor(j); + if (tile_data->is_valid_peering_bit_terrain(bit)) { + Vector polygon = tile_set->get_terrain_bit_polygon(tile_data->get_terrain_set(), bit); + if (Geometry2D::is_segment_intersecting_polygon(mm->get_position() - position, drag_last_pos - position, polygon)) { + tile_data->set_peering_bit_terrain(bit, terrain); + } + } } + } + } + drag_last_pos = mm->get_position(); + } + } + + Ref mb = p_event; + if (mb.is_valid()) { + if (mb->get_button_index() == MOUSE_BUTTON_LEFT) { + if (mb->is_pressed()) { + if (picker_button->is_pressed()) { + Vector3i tile = p_tile_atlas_view->get_alternative_tile_at_pos(mb->get_position()); + Vector2i coords = Vector2i(tile.x, tile.y); + int alternative_tile = tile.z; + + if (coords != TileSetSource::INVALID_ATLAS_COORDS) { + TileData *tile_data = Object::cast_to(p_tile_set_atlas_source->get_tile_data(coords, alternative_tile)); + int terrain_set = tile_data->get_terrain_set(); + Rect2i texture_region = p_tile_atlas_view->get_alternative_tile_rect(coords, alternative_tile); + Vector2i position = (texture_region.position + texture_region.get_end()) / 2 + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, alternative_tile); + dummy_object->set("terrain_set", terrain_set); + dummy_object->set("terrain", -1); + for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { + TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); + if (tile_set->is_valid_peering_bit_terrain(terrain_set, bit)) { + Vector polygon = tile_set->get_terrain_bit_polygon(terrain_set, bit); + if (Geometry2D::is_point_in_polygon(mb->get_position() - position, polygon)) { + dummy_object->set("terrain", tile_data->get_peering_bit_terrain(bit)); + } + } + } + terrain_set_property_editor->update_property(); + _update_terrain_selector(); + picker_button->set_pressed(false); + } + } else { + int terrain_set = int(dummy_object->get("terrain_set")); + int terrain = int(dummy_object->get("terrain")); + + Vector3i tile = p_tile_atlas_view->get_alternative_tile_at_pos(mb->get_position()); + Vector2i coords = Vector2i(tile.x, tile.y); + int alternative_tile = tile.z; - // Generate the polygon color, slightly randomly modified from the settings one. - Color random_variation_color; - random_variation_color.set_hsv(color.get_h() + rand.random(-1.0, 1.0) * 0.05, color.get_s(), color.get_v() + rand.random(-1.0, 1.0) * 0.1); - random_variation_color.a = color.a; - Vector colors; - colors.push_back(random_variation_color); + TileData *tile_data = Object::cast_to(p_tile_set_atlas_source->get_tile_data(coords, alternative_tile)); - RenderingServer::get_singleton()->canvas_item_add_polygon(p_canvas_item->get_canvas_item(), vertices, colors); + if (terrain_set == -1 || !tile_data || tile_data->get_terrain_set() != terrain_set) { + drag_type = DRAG_TYPE_PAINT_TERRAIN_SET; + drag_modified.clear(); + drag_painted_value = int(dummy_object->get("terrain_set")); + if (coords != TileSetSource::INVALID_ATLAS_COORDS) { + TileMapCell cell; + cell.source_id = 0; + cell.set_atlas_coords(coords); + cell.alternative_tile = alternative_tile; + Dictionary dict; + dict["terrain_set"] = tile_data->get_terrain_set(); + Array array; + for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { + TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); + array.push_back(tile_data->is_valid_peering_bit_terrain(bit) ? tile_data->get_peering_bit_terrain(bit) : -1); + } + dict["terrain_peering_bits"] = array; + drag_modified[cell] = dict; + tile_data->set_terrain_set(drag_painted_value); + } + drag_last_pos = mb->get_position(); + } else if (tile_data && tile_data->get_terrain_set() == terrain_set) { + // Paint terrain bits. + drag_type = DRAG_TYPE_PAINT_TERRAIN_BITS; + drag_modified.clear(); + Dictionary painted_dict; + painted_dict["terrain_set"] = terrain_set; + painted_dict["terrain"] = terrain; + drag_painted_value = painted_dict; + + if (coords != TileSetSource::INVALID_ATLAS_COORDS) { + TileMapCell cell; + cell.source_id = 0; + cell.set_atlas_coords(coords); + cell.alternative_tile = alternative_tile; + + // Save the old terrain_set and terrains bits. + Dictionary dict; + dict["terrain_set"] = tile_data->get_terrain_set(); + Array array; + for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { + TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); + array.push_back(tile_data->is_valid_peering_bit_terrain(bit) ? tile_data->get_peering_bit_terrain(bit) : -1); + } + dict["terrain_peering_bits"] = array; + drag_modified[cell] = dict; + + // Set the terrain bit. + Rect2i texture_region = p_tile_atlas_view->get_alternative_tile_rect(coords, alternative_tile); + Vector2i position = (texture_region.position + texture_region.get_end()) / 2 + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, alternative_tile); + for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { + TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); + if (tile_set->is_valid_peering_bit_terrain(terrain_set, bit)) { + Vector polygon = tile_set->get_terrain_bit_polygon(terrain_set, bit); + if (Geometry2D::is_point_in_polygon(mb->get_position() - position, polygon)) { + tile_data->set_peering_bit_terrain(bit, terrain); + } + } + } + } + drag_last_pos = mb->get_position(); + } } + } else { + if (drag_type == DRAG_TYPE_PAINT_TERRAIN_SET) { + undo_redo->create_action(TTR("Painting Tiles Property")); + for (Map::Element *E = drag_modified.front(); E; E = E->next()) { + Dictionary dict = E->get(); + Vector2i coords = E->key().get_atlas_coords(); + undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain_set", coords.x, coords.y, E->key().alternative_tile), dict["terrain_set"]); + undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain_set", coords.x, coords.y, E->key().alternative_tile), drag_painted_value); + Array array = dict["terrain_peering_bits"]; + for (int i = 0; i < array.size(); i++) { + undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E->key().alternative_tile), array[i]); + } + } + undo_redo->commit_action(false); + drag_type = DRAG_TYPE_NONE; + } else if (drag_type == DRAG_TYPE_PAINT_TERRAIN_BITS) { + Dictionary painted = Dictionary(drag_painted_value); + int terrain_set = int(painted["terrain_set"]); + int terrain = int(painted["terrain"]); + undo_redo->create_action(TTR("Painting Terrain")); + for (Map::Element *E = drag_modified.front(); E; E = E->next()) { + Dictionary dict = E->get(); + Vector2i coords = E->key().get_atlas_coords(); + Array array = dict["terrain_peering_bits"]; + for (int i = 0; i < array.size(); i++) { + TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); + if (tile_set->is_valid_peering_bit_terrain(terrain_set, bit)) { + undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E->key().alternative_tile), terrain); + } + if (tile_set->is_valid_peering_bit_terrain(dict["terrain_set"], bit)) { + undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E->key().alternative_tile), array[i]); + } + } + } + undo_redo->commit_action(false); + drag_type = DRAG_TYPE_NONE; + } + } + } + } +} + +void TileDataTerrainsEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected) { + TileData *tile_data = _get_tile_data(p_cell); + ERR_FAIL_COND(!tile_data); + + tile_set->draw_terrains(p_canvas_item, p_transform, tile_data); +} + +void TileDataTerrainsEditor::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: + case NOTIFICATION_THEME_CHANGED: + picker_button->set_icon(get_theme_icon("ColorPick", "EditorIcons")); + break; + default: + break; + } +} + +TileDataTerrainsEditor::TileDataTerrainsEditor() { + label = memnew(Label); + label->set_text("Painting:"); + add_child(label); + + // Toolbar + toolbar->add_child(memnew(VSeparator)); + + picker_button = memnew(Button); + picker_button->set_flat(true); + picker_button->set_toggle_mode(true); + picker_button->set_shortcut(ED_SHORTCUT("tiles_editor/picker", "Picker", KEY_P)); + toolbar->add_child(picker_button); + + // Setup + dummy_object->add_dummy_property("terrain_set"); + dummy_object->set("terrain_set", -1); + dummy_object->add_dummy_property("terrain"); + dummy_object->set("terrain", -1); + + // Get the default value for the type. + terrain_set_property_editor = memnew(EditorPropertyEnum); + terrain_set_property_editor->set_object_and_property(dummy_object, "terrain_set"); + terrain_set_property_editor->set_label("Terrain Set"); + terrain_set_property_editor->connect("property_changed", callable_mp(this, &TileDataTerrainsEditor::_property_value_changed).unbind(1)); + add_child(terrain_set_property_editor); + + terrain_property_editor = memnew(EditorPropertyEnum); + terrain_property_editor->set_object_and_property(dummy_object, "terrain"); + terrain_property_editor->set_label("Terrain"); + terrain_property_editor->connect("property_changed", callable_mp(this, &TileDataTerrainsEditor::_property_value_changed).unbind(1)); + add_child(terrain_property_editor); +} + +TileDataTerrainsEditor::~TileDataTerrainsEditor() { + toolbar->queue_delete(); + memdelete(dummy_object); +} + +Variant TileDataNavigationEditor::_get_painted_value() { + Ref navigation_polygon; + navigation_polygon.instantiate(); + + for (int i = 0; i < polygon_editor->get_polygon_count(); i++) { + Vector polygon = polygon_editor->get_polygon(i); + navigation_polygon->add_outline(polygon); + } + + navigation_polygon->make_polygons_from_outlines(); + return navigation_polygon; +} + +void TileDataNavigationEditor::_set_painted_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) { + TileData *tile_data = Object::cast_to(p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile)); + ERR_FAIL_COND(!tile_data); + + Ref navigation_polygon = tile_data->get_navigation_polygon(navigation_layer); + polygon_editor->clear_polygons(); + if (navigation_polygon.is_valid()) { + for (int i = 0; i < navigation_polygon->get_outline_count(); i++) { + polygon_editor->add_polygon(navigation_polygon->get_outline(i)); + } + } + polygon_editor->set_background(p_tile_set_atlas_source->get_texture(), p_tile_set_atlas_source->get_tile_texture_region(p_coords), p_tile_set_atlas_source->get_tile_effective_texture_offset(p_coords, p_alternative_tile), tile_data->get_flip_h(), tile_data->get_flip_v(), tile_data->get_transpose(), tile_data->get_modulate()); +} + +void TileDataNavigationEditor::_set_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile, Variant p_value) { + TileData *tile_data = Object::cast_to(p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile)); + ERR_FAIL_COND(!tile_data); + Ref navigation_polygon = p_value; + tile_data->set_navigation_polygon(navigation_layer, navigation_polygon); + + polygon_editor->set_background(p_tile_set_atlas_source->get_texture(), p_tile_set_atlas_source->get_tile_texture_region(p_coords), p_tile_set_atlas_source->get_tile_effective_texture_offset(p_coords, p_alternative_tile), tile_data->get_flip_h(), tile_data->get_flip_v(), tile_data->get_transpose(), tile_data->get_modulate()); +} + +Variant TileDataNavigationEditor::_get_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) { + TileData *tile_data = Object::cast_to(p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile)); + ERR_FAIL_COND_V(!tile_data, Variant()); + return tile_data->get_navigation_polygon(navigation_layer); +} + +void TileDataNavigationEditor::_setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, Map p_previous_values, Variant p_new_value) { + for (Map::Element *E = p_previous_values.front(); E; E = E->next()) { + Vector2i coords = E->key().get_atlas_coords(); + undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/navigation_layer_%d/polygon", coords.x, coords.y, E->key().alternative_tile, navigation_layer), E->get()); + undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/navigation_layer_%d/polygon", coords.x, coords.y, E->key().alternative_tile, navigation_layer), p_new_value); + } +} + +void TileDataNavigationEditor::_tile_set_changed() { + polygon_editor->set_tile_set(tile_set); +} + +void TileDataNavigationEditor::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: + polygon_editor->set_polygons_color(get_tree()->get_debug_navigation_color()); + break; + default: + break; + } +} + +TileDataNavigationEditor::TileDataNavigationEditor() { + polygon_editor = memnew(GenericTilePolygonEditor); + polygon_editor->set_multiple_polygon_mode(true); + add_child(polygon_editor); +} + +void TileDataNavigationEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected) { + TileData *tile_data = _get_tile_data(p_cell); + ERR_FAIL_COND(!tile_data); + + // Draw all shapes. + RenderingServer::get_singleton()->canvas_item_add_set_transform(p_canvas_item->get_canvas_item(), p_transform); + + Ref navigation_polygon = tile_data->get_navigation_polygon(navigation_layer); + if (navigation_polygon.is_valid()) { + Vector verts = navigation_polygon->get_vertices(); + if (verts.size() < 3) { + return; + } + + Color color = p_canvas_item->get_tree()->get_debug_navigation_color(); + if (p_selected) { + Color grid_color = EditorSettings::get_singleton()->get("editors/tiles_editor/grid_color"); + Color selection_color = Color().from_hsv(Math::fposmod(grid_color.get_h() + 0.5, 1.0), grid_color.get_s(), grid_color.get_v(), 1.0); + selection_color.a = 0.7; + color = selection_color; + } + + RandomPCG rand; + for (int i = 0; i < navigation_polygon->get_polygon_count(); i++) { + // An array of vertices for this polygon. + Vector polygon = navigation_polygon->get_polygon(i); + Vector vertices; + vertices.resize(polygon.size()); + for (int j = 0; j < polygon.size(); j++) { + ERR_FAIL_INDEX(polygon[j], verts.size()); + vertices.write[j] = verts[polygon[j]]; } - RenderingServer::get_singleton()->canvas_item_add_set_transform(p_canvas_item->get_canvas_item(), Transform2D()); + // Generate the polygon color, slightly randomly modified from the settings one. + Color random_variation_color; + random_variation_color.set_hsv(color.get_h() + rand.random(-1.0, 1.0) * 0.05, color.get_s(), color.get_v() + rand.random(-1.0, 1.0) * 0.1); + random_variation_color.a = color.a; + Vector colors; + colors.push_back(random_variation_color); + + RenderingServer::get_singleton()->canvas_item_add_polygon(p_canvas_item->get_canvas_item(), vertices, colors); } } + + RenderingServer::get_singleton()->canvas_item_add_set_transform(p_canvas_item->get_canvas_item(), Transform2D()); } diff --git a/editor/plugins/tiles/tile_data_editors.h b/editor/plugins/tiles/tile_data_editors.h index b82189e1ee..781f26cc02 100644 --- a/editor/plugins/tiles/tile_data_editors.h +++ b/editor/plugins/tiles/tile_data_editors.h @@ -31,87 +31,378 @@ #ifndef TILE_DATA_EDITORS_H #define TILE_DATA_EDITORS_H +#include "tile_atlas_view.h" + +#include "editor/editor_node.h" +#include "editor/editor_properties.h" + +#include "scene/gui/box_container.h" #include "scene/gui/control.h" +#include "scene/gui/label.h" #include "scene/resources/tile_set.h" -class TileDataEditor : public Control { - GDCLASS(TileDataEditor, Control); +class TileDataEditor : public VBoxContainer { + GDCLASS(TileDataEditor, VBoxContainer); + +private: + void _call_tile_set_changed(); protected: - TileData *tile_data; - String property; + Ref tile_set; + TileData *_get_tile_data(TileMapCell p_cell); + virtual void _tile_set_changed(){}; - TileData *_get_tile_data(TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile); + static void _bind_methods(); public: - // Edits a TileData property. - void edit(TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property); + void set_tile_set(Ref p_tile_set); + + // Input to handle painting. + virtual Control *get_toolbar() { return nullptr; }; + virtual void forward_draw_over_atlas(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_atlas_source, CanvasItem *p_canvas_item, Transform2D p_transform){}; + virtual void forward_draw_over_alternatives(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_atlas_source, CanvasItem *p_canvas_item, Transform2D p_transform){}; + virtual void forward_painting_atlas_gui_input(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_atlas_source, const Ref &p_event){}; + virtual void forward_painting_alternatives_gui_input(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_atlas_source, const Ref &p_event){}; - // Used to draw the value over a tile. - virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property){}; + // Used to draw the tile data property value over a tile. + virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected = false){}; }; -class TileDataTextureOffsetEditor : public TileDataEditor { - GDCLASS(TileDataTextureOffsetEditor, TileDataEditor); +class DummyObject : public Object { + GDCLASS(DummyObject, Object) +private: + Map properties; + +protected: + bool _set(const StringName &p_name, const Variant &p_value); + bool _get(const StringName &p_name, Variant &r_ret) const; public: - virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) override; + bool has_dummy_property(StringName p_name); + void add_dummy_property(StringName p_name); + void remove_dummy_property(StringName p_name); + void clear_dummy_properties(); }; -class TileDataIntegerEditor : public TileDataEditor { - GDCLASS(TileDataIntegerEditor, TileDataEditor); +class GenericTilePolygonEditor : public VBoxContainer { + GDCLASS(GenericTilePolygonEditor, VBoxContainer); + +private: + Ref tile_set; + LocalVector> polygons; + bool multiple_polygon_mode = false; + + UndoRedo *undo_redo = EditorNode::get_undo_redo(); + + // UI + int hovered_polygon_index = -1; + int hovered_point_index = -1; + int hovered_segment_index = -1; + Vector2 hovered_segment_point; + + enum DragType { + DRAG_TYPE_NONE, + DRAG_TYPE_DRAG_POINT, + DRAG_TYPE_CREATE_POINT, + DRAG_TYPE_PAN, + }; + DragType drag_type; + int drag_polygon_index; + int drag_point_index; + Vector2 drag_last_pos; + PackedVector2Array drag_old_polygon; + + HBoxContainer *toolbar; + Ref tools_button_group; + Button *button_create; + Button *button_edit; + Button *button_delete; + Button *button_pixel_snap; + MenuButton *button_advanced_menu; + + Vector in_creation_polygon; + + Panel *panel; + Control *base_control; + EditorZoomWidget *editor_zoom_widget; + Button *button_center_view; + Vector2 panning; + + Ref background_texture; + Rect2 background_region; + Vector2 background_offset; + bool background_h_flip; + bool background_v_flip; + bool background_transpose; + Color background_modulate; + + Color polygon_color = Color(1.0, 0.0, 0.0); + + enum AdvancedMenuOption { + RESET_TO_DEFAULT_TILE, + CLEAR_TILE, + }; + + void _base_control_draw(); + void _zoom_changed(); + void _advanced_menu_item_pressed(int p_item_pressed); + void _center_view(); + void _base_control_gui_input(Ref p_event); + + void _snap_to_tile_shape(Point2 &r_point, float &r_current_snapped_dist, float p_snap_dist); + void _snap_to_half_pixel(Point2 &r_point); + void _grab_polygon_point(Vector2 p_pos, const Transform2D &p_polygon_xform, int &r_polygon_index, int &r_point_index); + void _grab_polygon_segment_point(Vector2 p_pos, const Transform2D &p_polygon_xform, int &r_polygon_index, int &r_segment_index, Vector2 &r_point); + +protected: + void _notification(int p_what); + static void _bind_methods(); public: - virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) override; + void set_tile_set(Ref p_tile_set); + void set_background(Ref p_texture, Rect2 p_region = Rect2(), Vector2 p_offset = Vector2(), bool p_flip_h = false, bool p_flip_v = false, bool p_transpose = false, Color p_modulate = Color(1.0, 1.0, 1.0, 0.0)); + + int get_polygon_count(); + int add_polygon(Vector p_polygon, int p_index = -1); + void remove_polygon(int p_index); + void clear_polygons(); + void set_polygon(int p_polygon_index, Vector p_polygon); + Vector get_polygon(int p_polygon_index); + + void set_polygons_color(Color p_color); + void set_multiple_polygon_mode(bool p_multiple_polygon_mode); + + GenericTilePolygonEditor(); }; -class TileDataFloatEditor : public TileDataEditor { - GDCLASS(TileDataFloatEditor, TileDataEditor); +class TileDataDefaultEditor : public TileDataEditor { + GDCLASS(TileDataDefaultEditor, TileDataEditor); + +private: + // Toolbar + HBoxContainer *toolbar = memnew(HBoxContainer); + Button *picker_button; + + // UI + Ref tile_bool_checked; + Ref tile_bool_unchecked; + Label *label; + + EditorProperty *property_editor = nullptr; + + // Painting state. + enum DragType { + DRAG_TYPE_NONE = 0, + DRAG_TYPE_PAINT, + DRAG_TYPE_PAINT_RECT, + }; + DragType drag_type = DRAG_TYPE_NONE; + Vector2 drag_start_pos; + Vector2 drag_last_pos; + Map drag_modified; + Variant drag_painted_value; + + void _property_value_changed(StringName p_property, Variant p_value, StringName p_field); + +protected: + DummyObject *dummy_object = memnew(DummyObject); + + UndoRedo *undo_redo = EditorNode::get_undo_redo(); + + StringName type; + String property; + void _notification(int p_what); + + virtual Variant _get_painted_value(); + virtual void _set_painted_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile); + virtual void _set_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile, Variant p_value); + virtual Variant _get_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile); + virtual void _setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, Map p_previous_values, Variant p_new_value); public: - virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) override; + virtual Control *get_toolbar() override { return toolbar; }; + virtual void forward_draw_over_atlas(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_atlas_source, CanvasItem *p_canvas_item, Transform2D p_transform) override; + virtual void forward_draw_over_alternatives(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_atlas_source, CanvasItem *p_canvas_item, Transform2D p_transform) override; + virtual void forward_painting_atlas_gui_input(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_atlas_source, const Ref &p_event) override; + virtual void forward_painting_alternatives_gui_input(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_atlas_source, const Ref &p_event) override; + virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected = false) override; + + void setup_property_editor(Variant::Type p_type, String p_property, String p_label = "", Variant p_default_value = Variant()); + + TileDataDefaultEditor(); + ~TileDataDefaultEditor(); }; -class TileDataPositionEditor : public TileDataEditor { - GDCLASS(TileDataPositionEditor, TileDataEditor); +class TileDataTextureOffsetEditor : public TileDataDefaultEditor { + GDCLASS(TileDataTextureOffsetEditor, TileDataDefaultEditor); public: - virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) override; + virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected = false) override; }; -class TileDataYSortEditor : public TileDataEditor { - GDCLASS(TileDataYSortEditor, TileDataEditor); +class TileDataPositionEditor : public TileDataDefaultEditor { + GDCLASS(TileDataPositionEditor, TileDataDefaultEditor); public: - virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) override; + virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected = false) override; }; -class TileDataOcclusionShapeEditor : public TileDataEditor { - GDCLASS(TileDataOcclusionShapeEditor, TileDataEditor); +class TileDataYSortEditor : public TileDataDefaultEditor { + GDCLASS(TileDataYSortEditor, TileDataDefaultEditor); public: - virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) override; + virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected = false) override; }; -class TileDataCollisionShapeEditor : public TileDataEditor { - GDCLASS(TileDataCollisionShapeEditor, TileDataEditor); +class TileDataOcclusionShapeEditor : public TileDataDefaultEditor { + GDCLASS(TileDataOcclusionShapeEditor, TileDataDefaultEditor); + +private: + int occlusion_layer = -1; + + // UI + GenericTilePolygonEditor *polygon_editor; + + void _polygon_changed(PackedVector2Array p_polygon); + + virtual Variant _get_painted_value() override; + virtual void _set_painted_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) override; + virtual void _set_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile, Variant p_value) override; + virtual Variant _get_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) override; + virtual void _setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, Map p_previous_values, Variant p_new_value) override; + +protected: + UndoRedo *undo_redo = EditorNode::get_undo_redo(); + + virtual void _tile_set_changed() override; + + void _notification(int p_what); + +public: + virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected = false) override; + + void set_occlusion_layer(int p_occlusion_layer) { occlusion_layer = p_occlusion_layer; } + + TileDataOcclusionShapeEditor(); +}; + +class TileDataCollisionEditor : public TileDataDefaultEditor { + GDCLASS(TileDataCollisionEditor, TileDataDefaultEditor); + + int physics_layer = -1; + + // UI + GenericTilePolygonEditor *polygon_editor; + DummyObject *dummy_object = memnew(DummyObject); + Map property_editors; + + void _property_value_changed(StringName p_property, Variant p_value, StringName p_field); + void _polygons_changed(); + + virtual Variant _get_painted_value() override; + virtual void _set_painted_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) override; + virtual void _set_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile, Variant p_value) override; + virtual Variant _get_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) override; + virtual void _setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, Map p_previous_values, Variant p_new_value) override; + +protected: + UndoRedo *undo_redo = EditorNode::get_undo_redo(); + + virtual void _tile_set_changed() override; + + void _notification(int p_what); public: - virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) override; + virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected = false) override; + + void set_physics_layer(int p_physics_layer) { physics_layer = p_physics_layer; } + + TileDataCollisionEditor(); + ~TileDataCollisionEditor(); }; class TileDataTerrainsEditor : public TileDataEditor { GDCLASS(TileDataTerrainsEditor, TileDataEditor); +private: + // Toolbar + HBoxContainer *toolbar = memnew(HBoxContainer); + Button *picker_button; + + // Painting state. + enum DragType { + DRAG_TYPE_NONE = 0, + DRAG_TYPE_PAINT_TERRAIN_SET, + DRAG_TYPE_PAINT_TERRAIN_SET_RECT, + DRAG_TYPE_PAINT_TERRAIN_BITS, + DRAG_TYPE_PAINT_TERRAIN_BITS_RECT, + }; + DragType drag_type = DRAG_TYPE_NONE; + Vector2 drag_start_pos; + Vector2 drag_last_pos; + Map drag_modified; + Variant drag_painted_value; + + // UI + Label *label; + DummyObject *dummy_object = memnew(DummyObject); + EditorPropertyEnum *terrain_set_property_editor = nullptr; + EditorPropertyEnum *terrain_property_editor = nullptr; + + void _property_value_changed(StringName p_property, Variant p_value, StringName p_field); + + void _update_terrain_selector(); + +protected: + virtual void _tile_set_changed() override; + + void _notification(int p_what); + + UndoRedo *undo_redo = EditorNode::get_undo_redo(); + public: - virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) override; + virtual Control *get_toolbar() override { return toolbar; }; + virtual void forward_draw_over_atlas(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_atlas_source, CanvasItem *p_canvas_item, Transform2D p_transform) override; + virtual void forward_draw_over_alternatives(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_atlas_source, CanvasItem *p_canvas_item, Transform2D p_transform) override; + virtual void forward_painting_atlas_gui_input(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_atlas_source, const Ref &p_event) override; + virtual void forward_painting_alternatives_gui_input(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_atlas_source, const Ref &p_event) override; + virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected = false) override; + + TileDataTerrainsEditor(); + ~TileDataTerrainsEditor(); }; -class TileDataNavigationPolygonEditor : public TileDataEditor { - GDCLASS(TileDataNavigationPolygonEditor, TileDataEditor); +class TileDataNavigationEditor : public TileDataDefaultEditor { + GDCLASS(TileDataNavigationEditor, TileDataDefaultEditor); + +private: + int navigation_layer = -1; + PackedVector2Array navigation_polygon; + + // UI + GenericTilePolygonEditor *polygon_editor; + + void _polygon_changed(PackedVector2Array p_polygon); + + virtual Variant _get_painted_value() override; + virtual void _set_painted_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) override; + virtual void _set_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile, Variant p_value) override; + virtual Variant _get_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) override; + virtual void _setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, Map p_previous_values, Variant p_new_value) override; + +protected: + UndoRedo *undo_redo = EditorNode::get_undo_redo(); + + virtual void _tile_set_changed() override; + + void _notification(int p_what); public: - virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, String p_property) override; + virtual void draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected = false) override; + + void set_navigation_layer(int p_navigation_layer) { navigation_layer = p_navigation_layer; } + + TileDataNavigationEditor(); }; #endif // TILE_DATA_EDITORS_H diff --git a/editor/plugins/tiles/tile_map_editor.cpp b/editor/plugins/tiles/tile_map_editor.cpp index 7ad6462c0e..86bd115ac2 100644 --- a/editor/plugins/tiles/tile_map_editor.cpp +++ b/editor/plugins/tiles/tile_map_editor.cpp @@ -2989,6 +2989,7 @@ void TileMapEditorTerrainsPlugin::_update_terrains_tree() { } // Fill in the terrain list. + Vector>> icons = tile_set->generate_terrains_icons(Size2(16, 16) * EDSCALE); for (int terrain_set_index = 0; terrain_set_index < tile_set->get_terrain_sets_count(); terrain_set_index++) { // Add an item for the terrain set. TreeItem *terrain_set_tree_item = terrains_tree->create_item(); @@ -3007,58 +3008,12 @@ void TileMapEditorTerrainsPlugin::_update_terrains_tree() { terrain_set_tree_item->set_selectable(0, false); for (int terrain_index = 0; terrain_index < tile_set->get_terrains_count(terrain_set_index); terrain_index++) { - // Compute the terrains_tile_pattern used for terrain preview (whenever possible). - TerrainsTilePattern terrains_tile_pattern; - int max_bit_count = -1; - for (Set::Element *E = per_terrain_terrains_tile_patterns[terrain_set_index][terrain_index].front(); E; E = E->next()) { - int count = 0; - for (int i = 0; i < E->get().size(); i++) { - if (int(E->get()[i]) == terrain_index) { - count++; - } - } - if (count > max_bit_count) { - terrains_tile_pattern = E->get(); - max_bit_count = count; - } - } - - // Get the preview. - Ref icon; - Rect2 region; - if (max_bit_count >= 0) { - double max_probability = -1.0; - for (Set::Element *E = per_terrain_terrains_tile_patterns_tiles[terrain_set_index][terrains_tile_pattern].front(); E; E = E->next()) { - Ref source = tile_set->get_source(E->get().source_id); - - Ref atlas_source = source; - if (atlas_source.is_valid()) { - TileData *tile_data = Object::cast_to(atlas_source->get_tile_data(E->get().get_atlas_coords(), E->get().alternative_tile)); - if (tile_data->get_probability() > max_probability) { - icon = atlas_source->get_texture(); - region = atlas_source->get_tile_texture_region(E->get().get_atlas_coords()); - max_probability = tile_data->get_probability(); - } - } - } - } else { - Ref image; - image.instantiate(); - image->create(1, 1, false, Image::FORMAT_RGBA8); - image->set_pixel(0, 0, tile_set->get_terrain_color(terrain_set_index, terrain_index)); - Ref image_texture; - image_texture.instantiate(); - image_texture->create_from_image(image); - image_texture->set_size_override(Size2(32, 32) * EDSCALE); - icon = image_texture; - } - // Add the item to the terrain list. TreeItem *terrain_tree_item = terrains_tree->create_item(terrain_set_tree_item); terrain_tree_item->set_text(0, tile_set->get_terrain_name(terrain_set_index, terrain_index)); terrain_tree_item->set_icon_max_width(0, 32 * EDSCALE); - terrain_tree_item->set_icon(0, icon); - terrain_tree_item->set_icon_region(0, region); + terrain_tree_item->set_icon(0, icons[terrain_set_index][terrain_index]); + Dictionary metadata_dict; metadata_dict["terrain_set"] = terrain_set_index; metadata_dict["terrain_id"] = terrain_index; diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp index f6007afe4e..9d849a0df5 100644 --- a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp +++ b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp @@ -357,6 +357,7 @@ void TileSetAtlasSourceEditor::AtlasTileProxyObject::_bind_methods() { void TileSetAtlasSourceEditor::_inspector_property_selected(String p_property) { selected_property = p_property; _update_atlas_view(); + _update_current_tile_data_editor(); } void TileSetAtlasSourceEditor::_update_tile_id_label() { @@ -398,17 +399,315 @@ void TileSetAtlasSourceEditor::_update_fix_selected_and_hovered_tiles() { } } +void TileSetAtlasSourceEditor::_update_atlas_source_inspector() { + // Update visibility. + bool visible = tools_button_group->get_pressed_button() == tool_setup_atlas_source_button; + atlas_source_inspector_label->set_visible(visible); + atlas_source_inspector->set_visible(visible); +} + void TileSetAtlasSourceEditor::_update_tile_inspector() { - bool has_atlas_tile_selected = (tools_button_group->get_pressed_button() == tool_select_button) && !selection.is_empty(); + // Update visibility. + if (tools_button_group->get_pressed_button() == tool_select_button) { + if (!selection.is_empty()) { + tile_proxy_object->edit(tile_set_atlas_source, selection); + } + tile_inspector_label->show(); + tile_inspector->set_visible(!selection.is_empty()); + tile_inspector_no_tile_selected_label->set_visible(selection.is_empty()); + } else { + tile_inspector_label->hide(); + tile_inspector->hide(); + tile_inspector_no_tile_selected_label->hide(); + } +} - // Update the proxy object. - if (has_atlas_tile_selected) { - tile_proxy_object->edit(tile_set_atlas_source, selection); +void TileSetAtlasSourceEditor::_update_tile_data_editors() { + String previously_selected; + if (tile_data_editors_tree && tile_data_editors_tree->get_selected()) { + previously_selected = tile_data_editors_tree->get_selected()->get_metadata(0); + } + + tile_data_editors_tree->clear(); + + TreeItem *root = tile_data_editors_tree->create_item(); + + TreeItem *group; +#define ADD_TILE_DATA_EDITOR_GROUP(text) \ + group = tile_data_editors_tree->create_item(root); \ + group->set_custom_bg_color(0, group_color); \ + group->set_selectable(0, false); \ + group->set_disable_folding(true); \ + group->set_text(0, text); + + TreeItem *item; +#define ADD_TILE_DATA_EDITOR(parent, text, property) \ + item = tile_data_editors_tree->create_item(parent); \ + item->set_text(0, text); \ + item->set_metadata(0, property); \ + if (property == previously_selected) { \ + item->select(0); \ + } + + // Theming. + tile_data_editors_tree->add_theme_constant_override("vseparation", 1); + tile_data_editors_tree->add_theme_constant_override("hseparation", 3); + + Color group_color = get_theme_color("prop_category", "Editor"); + + // List of editors. + // --- Rendering --- + ADD_TILE_DATA_EDITOR_GROUP("Rendering"); + + ADD_TILE_DATA_EDITOR(group, "Texture Offset", "texture_offset"); + if (!tile_data_editors.has("texture_offset")) { + TileDataTextureOffsetEditor *tile_data_texture_offset_editor = memnew(TileDataTextureOffsetEditor); + tile_data_texture_offset_editor->hide(); + tile_data_texture_offset_editor->setup_property_editor(Variant::VECTOR2, "texture_offset"); + tile_data_texture_offset_editor->connect("needs_redraw", callable_mp((CanvasItem *)tile_atlas_control_unscaled, &Control::update)); + tile_data_texture_offset_editor->connect("needs_redraw", callable_mp((CanvasItem *)alternative_tiles_control_unscaled, &Control::update)); + tile_data_editors["texture_offset"] = tile_data_texture_offset_editor; + } + + ADD_TILE_DATA_EDITOR(group, "Modulate", "modulate"); + if (!tile_data_editors.has("modulate")) { + TileDataDefaultEditor *tile_data_modulate_editor = memnew(TileDataDefaultEditor()); + tile_data_modulate_editor->hide(); + tile_data_modulate_editor->setup_property_editor(Variant::COLOR, "modulate", "", Color(1.0, 1.0, 1.0, 1.0)); + tile_data_modulate_editor->connect("needs_redraw", callable_mp((CanvasItem *)tile_atlas_control_unscaled, &Control::update)); + tile_data_modulate_editor->connect("needs_redraw", callable_mp((CanvasItem *)alternative_tiles_control_unscaled, &Control::update)); + tile_data_editors["modulate"] = tile_data_modulate_editor; + } + + ADD_TILE_DATA_EDITOR(group, "Z Index", "z_index"); + if (!tile_data_editors.has("z_index")) { + TileDataDefaultEditor *tile_data_z_index_editor = memnew(TileDataDefaultEditor()); + tile_data_z_index_editor->hide(); + tile_data_z_index_editor->setup_property_editor(Variant::INT, "z_index"); + tile_data_z_index_editor->connect("needs_redraw", callable_mp((CanvasItem *)tile_atlas_control_unscaled, &Control::update)); + tile_data_z_index_editor->connect("needs_redraw", callable_mp((CanvasItem *)alternative_tiles_control_unscaled, &Control::update)); + tile_data_editors["z_index"] = tile_data_z_index_editor; + } + + ADD_TILE_DATA_EDITOR(group, "Y Sort Origin", "y_sort_origin"); + if (!tile_data_editors.has("y_sort_origin")) { + TileDataYSortEditor *tile_data_y_sort_editor = memnew(TileDataYSortEditor); + tile_data_y_sort_editor->hide(); + tile_data_y_sort_editor->setup_property_editor(Variant::INT, "y_sort_origin"); + tile_data_y_sort_editor->connect("needs_redraw", callable_mp((CanvasItem *)tile_atlas_control_unscaled, &Control::update)); + tile_data_y_sort_editor->connect("needs_redraw", callable_mp((CanvasItem *)alternative_tiles_control_unscaled, &Control::update)); + tile_data_editors["y_sort_origin"] = tile_data_y_sort_editor; + } + + for (int i = 0; i < tile_set->get_occlusion_layers_count(); i++) { + ADD_TILE_DATA_EDITOR(group, vformat("Occlusion Layer %d", i), vformat("occlusion_layer_%d", i)); + if (!tile_data_editors.has(vformat("occlusion_layer_%d", i))) { + TileDataOcclusionShapeEditor *tile_data_occlusion_shape_editor = memnew(TileDataOcclusionShapeEditor()); + tile_data_occlusion_shape_editor->hide(); + tile_data_occlusion_shape_editor->set_occlusion_layer(i); + tile_data_occlusion_shape_editor->connect("needs_redraw", callable_mp((CanvasItem *)tile_atlas_control_unscaled, &Control::update)); + tile_data_occlusion_shape_editor->connect("needs_redraw", callable_mp((CanvasItem *)alternative_tiles_control_unscaled, &Control::update)); + tile_data_editors[vformat("occlusion_layer_%d", i)] = tile_data_occlusion_shape_editor; + } + } + for (int i = tile_set->get_occlusion_layers_count(); tile_data_editors.has(vformat("occlusion_layer_%d", i)); i++) { + tile_data_editors[vformat("occlusion_layer_%d", i)]->queue_delete(); + tile_data_editors.erase(vformat("occlusion_layer_%d", i)); + } + + // --- Rendering --- + ADD_TILE_DATA_EDITOR(root, "Terrains", "terrain_set"); + if (!tile_data_editors.has("terrain_set")) { + TileDataTerrainsEditor *tile_data_terrains_editor = memnew(TileDataTerrainsEditor); + tile_data_terrains_editor->hide(); + tile_data_terrains_editor->connect("needs_redraw", callable_mp((CanvasItem *)tile_atlas_control_unscaled, &Control::update)); + tile_data_terrains_editor->connect("needs_redraw", callable_mp((CanvasItem *)alternative_tiles_control_unscaled, &Control::update)); + tile_data_editors["terrain_set"] = tile_data_terrains_editor; + } + + // --- Miscellaneous --- + ADD_TILE_DATA_EDITOR(root, "Probability", "probability"); + if (!tile_data_editors.has("probability")) { + TileDataDefaultEditor *tile_data_probability_editor = memnew(TileDataDefaultEditor()); + tile_data_probability_editor->hide(); + tile_data_probability_editor->setup_property_editor(Variant::FLOAT, "probability", "", 1.0); + tile_data_probability_editor->connect("needs_redraw", callable_mp((CanvasItem *)tile_atlas_control_unscaled, &Control::update)); + tile_data_probability_editor->connect("needs_redraw", callable_mp((CanvasItem *)alternative_tiles_control_unscaled, &Control::update)); + tile_data_editors["probability"] = tile_data_probability_editor; + } + + // --- Physics --- + ADD_TILE_DATA_EDITOR_GROUP("Physics"); + for (int i = 0; i < tile_set->get_physics_layers_count(); i++) { + ADD_TILE_DATA_EDITOR(group, vformat("Physics Layer %d", i), vformat("physics_layer_%d", i)); + if (!tile_data_editors.has(vformat("physics_layer_%d", i))) { + TileDataCollisionEditor *tile_data_collision_editor = memnew(TileDataCollisionEditor()); + tile_data_collision_editor->hide(); + tile_data_collision_editor->set_physics_layer(i); + tile_data_collision_editor->connect("needs_redraw", callable_mp((CanvasItem *)tile_atlas_control_unscaled, &Control::update)); + tile_data_collision_editor->connect("needs_redraw", callable_mp((CanvasItem *)alternative_tiles_control_unscaled, &Control::update)); + tile_data_editors[vformat("physics_layer_%d", i)] = tile_data_collision_editor; + } + } + for (int i = tile_set->get_physics_layers_count(); tile_data_editors.has(vformat("physics_layer_%d", i)); i++) { + tile_data_editors[vformat("physics_layer_%d", i)]->queue_delete(); + tile_data_editors.erase(vformat("physics_layer_%d", i)); + } + + // --- Navigation --- + ADD_TILE_DATA_EDITOR_GROUP("Navigation"); + for (int i = 0; i < tile_set->get_navigation_layers_count(); i++) { + ADD_TILE_DATA_EDITOR(group, vformat("Navigation Layer %d", i), vformat("navigation_layer_%d", i)); + if (!tile_data_editors.has(vformat("navigation_layer_%d", i))) { + TileDataNavigationEditor *tile_data_navigation_editor = memnew(TileDataNavigationEditor()); + tile_data_navigation_editor->hide(); + tile_data_navigation_editor->set_navigation_layer(i); + tile_data_navigation_editor->connect("needs_redraw", callable_mp((CanvasItem *)tile_atlas_control_unscaled, &Control::update)); + tile_data_navigation_editor->connect("needs_redraw", callable_mp((CanvasItem *)alternative_tiles_control_unscaled, &Control::update)); + tile_data_editors[vformat("navigation_layer_%d", i)] = tile_data_navigation_editor; + } + } + for (int i = tile_set->get_navigation_layers_count(); tile_data_editors.has(vformat("navigation_layer_%d", i)); i++) { + tile_data_editors[vformat("navigation_layer_%d", i)]->queue_delete(); + tile_data_editors.erase(vformat("navigation_layer_%d", i)); + } + + // --- Custom Data --- + ADD_TILE_DATA_EDITOR_GROUP("Custom Data"); + for (int i = 0; i < tile_set->get_custom_data_layers_count(); i++) { + if (tile_set->get_custom_data_name(i).is_empty()) { + ADD_TILE_DATA_EDITOR(group, vformat("Custom Data %d", i), vformat("custom_data_%d", i)); + } else { + ADD_TILE_DATA_EDITOR(group, tile_set->get_custom_data_name(i), vformat("custom_data_%d", i)); + } + if (!tile_data_editors.has(vformat("custom_data_%d", i))) { + TileDataDefaultEditor *tile_data_custom_data_editor = memnew(TileDataDefaultEditor()); + tile_data_custom_data_editor->hide(); + tile_data_custom_data_editor->setup_property_editor(tile_set->get_custom_data_type(i), vformat("custom_data_%d", i), tile_set->get_custom_data_name(i)); + tile_data_custom_data_editor->connect("needs_redraw", callable_mp((CanvasItem *)tile_atlas_control_unscaled, &Control::update)); + tile_data_custom_data_editor->connect("needs_redraw", callable_mp((CanvasItem *)alternative_tiles_control_unscaled, &Control::update)); + tile_data_editors[vformat("custom_data_%d", i)] = tile_data_custom_data_editor; + } + } + for (int i = tile_set->get_custom_data_layers_count(); tile_data_editors.has(vformat("custom_data_%d", i)); i++) { + tile_data_editors[vformat("custom_data_%d", i)]->queue_delete(); + tile_data_editors.erase(vformat("custom_data_%d", i)); + } + +#undef ADD_TILE_DATA_EDITOR_GROUP +#undef ADD_TILE_DATA_EDITOR + + // Add tile data editors as children. + for (Map::Element *E = tile_data_editors.front(); E; E = E->next()) { + // Tile Data Editor. + TileDataEditor *tile_data_editor = E->get(); + if (!tile_data_editor->is_inside_tree()) { + tile_data_painting_editor_container->add_child(tile_data_editor); + } + tile_data_editor->set_tile_set(tile_set); + + // Toolbar. + Control *toolbar = tile_data_editor->get_toolbar(); + if (!toolbar->is_inside_tree()) { + tool_settings_tile_data_toolbar_container->add_child(toolbar); + } + toolbar->hide(); } // Update visibility. - tile_inspector_label->set_visible(has_atlas_tile_selected); - tile_inspector->set_visible(has_atlas_tile_selected); + bool is_visible = tools_button_group->get_pressed_button() == tool_paint_button; + tile_data_editor_dropdown_button->set_visible(is_visible); + tile_data_editor_dropdown_button->set_text(TTR("Select a property editor")); + tile_data_editors_label->set_visible(is_visible); +} + +void TileSetAtlasSourceEditor::_update_current_tile_data_editor() { + // Find the property to use. + String property; + if (tools_button_group->get_pressed_button() == tool_select_button && tile_inspector->is_visible() && !tile_inspector->get_selected_path().is_empty()) { + Vector components = tile_inspector->get_selected_path().split("/"); + if (components.size() >= 1) { + property = components[0]; + + // Workaround for terrains as they don't have a common first component. + if (property.begins_with("terrains_")) { + property = "terrain_set"; + } + } + } else if (tools_button_group->get_pressed_button() == tool_paint_button && tile_data_editors_tree->get_selected()) { + property = tile_data_editors_tree->get_selected()->get_metadata(0); + tile_data_editor_dropdown_button->set_text(tile_data_editors_tree->get_selected()->get_text(0)); + } + + // Hide all editors but the current one. + for (Map::Element *E = tile_data_editors.front(); E; E = E->next()) { + E->get()->hide(); + E->get()->get_toolbar()->hide(); + } + if (tile_data_editors.has(property)) { + current_tile_data_editor = tile_data_editors[property]; + } else { + current_tile_data_editor = nullptr; + } + + // Get the correct editor for the TileData's property. + if (current_tile_data_editor) { + current_tile_data_editor_toolbar = current_tile_data_editor->get_toolbar(); + current_property = property; + current_tile_data_editor->set_visible(tools_button_group->get_pressed_button() == tool_paint_button); + current_tile_data_editor_toolbar->set_visible(tools_button_group->get_pressed_button() == tool_paint_button); + } +} + +void TileSetAtlasSourceEditor::_tile_data_editor_dropdown_button_draw() { + if (!has_theme_icon("arrow", "OptionButton")) { + return; + } + + RID ci = tile_data_editor_dropdown_button->get_canvas_item(); + Ref arrow = Control::get_theme_icon("arrow", "OptionButton"); + Color clr = Color(1, 1, 1); + if (get_theme_constant("modulate_arrow")) { + switch (tile_data_editor_dropdown_button->get_draw_mode()) { + case BaseButton::DRAW_PRESSED: + clr = get_theme_color("font_pressed_color"); + break; + case BaseButton::DRAW_HOVER: + clr = get_theme_color("font_hover_color"); + break; + case BaseButton::DRAW_DISABLED: + clr = get_theme_color("font_disabled_color"); + break; + default: + clr = get_theme_color("font_color"); + } + } + + Size2 size = tile_data_editor_dropdown_button->get_size(); + + Point2 ofs; + if (is_layout_rtl()) { + ofs = Point2(get_theme_constant("arrow_margin", "OptionButton"), int(Math::abs((size.height - arrow->get_height()) / 2))); + } else { + ofs = Point2(size.width - arrow->get_width() - get_theme_constant("arrow_margin", "OptionButton"), int(Math::abs((size.height - arrow->get_height()) / 2))); + } + arrow->draw(ci, ofs, clr); +} + +void TileSetAtlasSourceEditor::_tile_data_editor_dropdown_button_pressed() { + Size2 size = tile_data_editor_dropdown_button->get_size(); + tile_data_editors_popup->set_position(tile_data_editor_dropdown_button->get_screen_position() + Size2(0, size.height * get_global_transform().get_scale().y)); + tile_data_editors_popup->set_size(Size2(size.width, 0)); + tile_data_editors_popup->popup(); +} + +void TileSetAtlasSourceEditor::_tile_data_editors_tree_selected() { + tile_data_editors_popup->call_deferred("hide"); + _update_current_tile_data_editor(); + tile_atlas_control->update(); + tile_atlas_control_unscaled->update(); + alternative_tiles_control->update(); + alternative_tiles_control_unscaled->update(); } void TileSetAtlasSourceEditor::_update_atlas_view() { @@ -467,19 +766,28 @@ void TileSetAtlasSourceEditor::_update_atlas_view() { } void TileSetAtlasSourceEditor::_update_toolbar() { - // Hide all settings. - for (int i = 0; i < tool_settings->get_child_count(); i++) { - Object::cast_to(tool_settings->get_child(i))->hide(); - } - - // SHow only the correct settings. - if (tools_button_group->get_pressed_button() == tool_select_button) { - } else if (tools_button_group->get_pressed_button() == tool_add_remove_button) { - tool_settings_vsep->show(); - tools_settings_erase_button->show(); - } else if (tools_button_group->get_pressed_button() == tool_add_remove_rect_button) { + // Show the tools and settings. + if (tools_button_group->get_pressed_button() == tool_setup_atlas_source_button) { + if (current_tile_data_editor_toolbar) { + current_tile_data_editor_toolbar->hide(); + } tool_settings_vsep->show(); tools_settings_erase_button->show(); + tool_advanced_menu_buttom->show(); + } else if (tools_button_group->get_pressed_button() == tool_select_button) { + if (current_tile_data_editor_toolbar) { + current_tile_data_editor_toolbar->hide(); + } + tool_settings_vsep->hide(); + tools_settings_erase_button->hide(); + tool_advanced_menu_buttom->hide(); + } else if (tools_button_group->get_pressed_button() == tool_paint_button) { + if (current_tile_data_editor_toolbar) { + current_tile_data_editor_toolbar->show(); + } + tool_settings_vsep->hide(); + tools_settings_erase_button->hide(); + tool_advanced_menu_buttom->hide(); } } @@ -499,357 +807,336 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_gui_input(const Refget_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position()); - // Handle the event. - Ref mm = p_event; - if (mm.is_valid()) { - Vector2i start_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_mouse_pos); - Vector2i last_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(drag_last_mouse_pos); - Vector2i new_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position()); + // Forward the event to the current tile data editor if we are in the painting mode. + if (tools_button_group->get_pressed_button() == tool_paint_button) { + if (current_tile_data_editor) { + current_tile_data_editor->forward_painting_atlas_gui_input(tile_atlas_view, tile_set_atlas_source, p_event); + } + // Update only what's needed. + tile_set_atlas_source_changed_needs_update = false; - Vector2i grid_size = tile_set_atlas_source->get_atlas_grid_size(); + tile_atlas_control->update(); + tile_atlas_control_unscaled->update(); + alternative_tiles_control->update(); + alternative_tiles_control_unscaled->update(); + tile_atlas_view->update(); + return; + } else { + // Handle the event. + Ref mm = p_event; + if (mm.is_valid()) { + Vector2i start_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_mouse_pos); + Vector2i last_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(drag_last_mouse_pos); + Vector2i new_base_tiles_coords = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position()); - if (drag_type == DRAG_TYPE_NONE) { - if (selection.size() == 1) { - // Change the cursor depending on the hovered thing. - TileSelection selected = selection.front()->get(); - if (selected.tile != TileSetSource::INVALID_ATLAS_COORDS && selected.alternative == 0) { - Vector2 mouse_local_pos = tile_atlas_control->get_local_mouse_position(); - Vector2i size_in_atlas = tile_set_atlas_source->get_tile_size_in_atlas(selected.tile); - Rect2 region = tile_set_atlas_source->get_tile_texture_region(selected.tile); - Size2 zoomed_size = resize_handle->get_size() / tile_atlas_view->get_zoom(); - Rect2 rect = region.grow_individual(zoomed_size.x, zoomed_size.y, 0, 0); - const Vector2i coords[] = { Vector2i(0, 0), Vector2i(1, 0), Vector2i(1, 1), Vector2i(0, 1) }; - const Vector2i directions[] = { Vector2i(0, -1), Vector2i(1, 0), Vector2i(0, 1), Vector2i(-1, 0) }; - CursorShape cursor_shape = CURSOR_ARROW; - bool can_grow[4]; - for (int i = 0; i < 4; i++) { - can_grow[i] = tile_set_atlas_source->can_move_tile_in_atlas(selected.tile, selected.tile + directions[i]); - can_grow[i] |= (i % 2 == 0) ? size_in_atlas.y > 1 : size_in_atlas.x > 1; - } - for (int i = 0; i < 4; i++) { - Vector2 pos = rect.position + Vector2(rect.size.x, rect.size.y) * coords[i]; - if (can_grow[i] && can_grow[(i + 3) % 4] && Rect2(pos, zoomed_size).has_point(mouse_local_pos)) { - cursor_shape = (i % 2) ? CURSOR_BDIAGSIZE : CURSOR_FDIAGSIZE; + Vector2i grid_size = tile_set_atlas_source->get_atlas_grid_size(); + + if (drag_type == DRAG_TYPE_NONE) { + if (selection.size() == 1) { + // Change the cursor depending on the hovered thing. + TileSelection selected = selection.front()->get(); + if (selected.tile != TileSetSource::INVALID_ATLAS_COORDS && selected.alternative == 0) { + Vector2 mouse_local_pos = tile_atlas_control->get_local_mouse_position(); + Vector2i size_in_atlas = tile_set_atlas_source->get_tile_size_in_atlas(selected.tile); + Rect2 region = tile_set_atlas_source->get_tile_texture_region(selected.tile); + Size2 zoomed_size = resize_handle->get_size() / tile_atlas_view->get_zoom(); + Rect2 rect = region.grow_individual(zoomed_size.x, zoomed_size.y, 0, 0); + const Vector2i coords[] = { Vector2i(0, 0), Vector2i(1, 0), Vector2i(1, 1), Vector2i(0, 1) }; + const Vector2i directions[] = { Vector2i(0, -1), Vector2i(1, 0), Vector2i(0, 1), Vector2i(-1, 0) }; + CursorShape cursor_shape = CURSOR_ARROW; + bool can_grow[4]; + for (int i = 0; i < 4; i++) { + can_grow[i] = tile_set_atlas_source->can_move_tile_in_atlas(selected.tile, selected.tile + directions[i]); + can_grow[i] |= (i % 2 == 0) ? size_in_atlas.y > 1 : size_in_atlas.x > 1; } - Vector2 next_pos = rect.position + Vector2(rect.size.x, rect.size.y) * coords[(i + 1) % 4]; - if (can_grow[i] && Rect2((pos + next_pos) / 2.0, zoomed_size).has_point(mouse_local_pos)) { - cursor_shape = (i % 2) ? CURSOR_HSIZE : CURSOR_VSIZE; + for (int i = 0; i < 4; i++) { + Vector2 pos = rect.position + Vector2(rect.size.x, rect.size.y) * coords[i]; + if (can_grow[i] && can_grow[(i + 3) % 4] && Rect2(pos, zoomed_size).has_point(mouse_local_pos)) { + cursor_shape = (i % 2) ? CURSOR_BDIAGSIZE : CURSOR_FDIAGSIZE; + } + Vector2 next_pos = rect.position + Vector2(rect.size.x, rect.size.y) * coords[(i + 1) % 4]; + if (can_grow[i] && Rect2((pos + next_pos) / 2.0, zoomed_size).has_point(mouse_local_pos)) { + cursor_shape = (i % 2) ? CURSOR_HSIZE : CURSOR_VSIZE; + } } + tile_atlas_control->set_default_cursor_shape(cursor_shape); } - tile_atlas_control->set_default_cursor_shape(cursor_shape); } - } - } else if (drag_type == DRAG_TYPE_CREATE_BIG_TILE) { - // Create big tile. - new_base_tiles_coords = new_base_tiles_coords.max(Vector2i(0, 0)).min(grid_size - Vector2i(1, 1)); - - Rect2i new_rect = Rect2i(start_base_tiles_coords, new_base_tiles_coords - start_base_tiles_coords).abs(); - new_rect.size += Vector2i(1, 1); - // Check if the new tile can fit in the new rect. - if (tile_set_atlas_source->can_move_tile_in_atlas(drag_current_tile, new_rect.position, new_rect.size)) { - // Move and resize the tile. - tile_set_atlas_source->move_tile_in_atlas(drag_current_tile, new_rect.position, new_rect.size); - drag_current_tile = new_rect.position; - } - } else if (drag_type == DRAG_TYPE_CREATE_TILES) { - // Create tiles. - last_base_tiles_coords = last_base_tiles_coords.max(Vector2i(0, 0)).min(grid_size - Vector2i(1, 1)); - new_base_tiles_coords = new_base_tiles_coords.max(Vector2i(0, 0)).min(grid_size - Vector2i(1, 1)); - - Vector line = Geometry2D::bresenham_line(last_base_tiles_coords, new_base_tiles_coords); - for (int i = 0; i < line.size(); i++) { - if (tile_set_atlas_source->get_tile_at_coords(line[i]) == TileSetSource::INVALID_ATLAS_COORDS) { - tile_set_atlas_source->create_tile(line[i]); - drag_modified_tiles.insert(line[i]); + } else if (drag_type == DRAG_TYPE_CREATE_BIG_TILE) { + // Create big tile. + new_base_tiles_coords = new_base_tiles_coords.max(Vector2i(0, 0)).min(grid_size - Vector2i(1, 1)); + + Rect2i new_rect = Rect2i(start_base_tiles_coords, new_base_tiles_coords - start_base_tiles_coords).abs(); + new_rect.size += Vector2i(1, 1); + // Check if the new tile can fit in the new rect. + if (tile_set_atlas_source->can_move_tile_in_atlas(drag_current_tile, new_rect.position, new_rect.size)) { + // Move and resize the tile. + tile_set_atlas_source->move_tile_in_atlas(drag_current_tile, new_rect.position, new_rect.size); + drag_current_tile = new_rect.position; + } + } else if (drag_type == DRAG_TYPE_CREATE_TILES) { + // Create tiles. + last_base_tiles_coords = last_base_tiles_coords.max(Vector2i(0, 0)).min(grid_size - Vector2i(1, 1)); + new_base_tiles_coords = new_base_tiles_coords.max(Vector2i(0, 0)).min(grid_size - Vector2i(1, 1)); + + Vector line = Geometry2D::bresenham_line(last_base_tiles_coords, new_base_tiles_coords); + for (int i = 0; i < line.size(); i++) { + if (tile_set_atlas_source->get_tile_at_coords(line[i]) == TileSetSource::INVALID_ATLAS_COORDS) { + tile_set_atlas_source->create_tile(line[i]); + drag_modified_tiles.insert(line[i]); + } } - } - drag_last_mouse_pos = tile_atlas_control->get_local_mouse_position(); + drag_last_mouse_pos = tile_atlas_control->get_local_mouse_position(); - } else if (drag_type == DRAG_TYPE_REMOVE_TILES) { - // Remove tiles. - last_base_tiles_coords = last_base_tiles_coords.max(Vector2i(0, 0)).min(grid_size - Vector2i(1, 1)); - new_base_tiles_coords = new_base_tiles_coords.max(Vector2i(0, 0)).min(grid_size - Vector2i(1, 1)); + } else if (drag_type == DRAG_TYPE_REMOVE_TILES) { + // Remove tiles. + last_base_tiles_coords = last_base_tiles_coords.max(Vector2i(0, 0)).min(grid_size - Vector2i(1, 1)); + new_base_tiles_coords = new_base_tiles_coords.max(Vector2i(0, 0)).min(grid_size - Vector2i(1, 1)); - Vector line = Geometry2D::bresenham_line(last_base_tiles_coords, new_base_tiles_coords); - for (int i = 0; i < line.size(); i++) { - Vector2i base_tile_coords = tile_set_atlas_source->get_tile_at_coords(line[i]); - if (base_tile_coords != TileSetSource::INVALID_ATLAS_COORDS) { - drag_modified_tiles.insert(base_tile_coords); + Vector line = Geometry2D::bresenham_line(last_base_tiles_coords, new_base_tiles_coords); + for (int i = 0; i < line.size(); i++) { + Vector2i base_tile_coords = tile_set_atlas_source->get_tile_at_coords(line[i]); + if (base_tile_coords != TileSetSource::INVALID_ATLAS_COORDS) { + drag_modified_tiles.insert(base_tile_coords); + } } - } - drag_last_mouse_pos = tile_atlas_control->get_local_mouse_position(); - } else if (drag_type == DRAG_TYPE_MOVE_TILE) { - // Move tile. - Vector2 mouse_offset = (Vector2(tile_set_atlas_source->get_tile_size_in_atlas(drag_current_tile)) / 2.0 - Vector2(0.5, 0.5)) * tile_set->get_tile_size(); - Vector2i coords = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position() - mouse_offset); - coords = coords.max(Vector2i(0, 0)).min(grid_size - Vector2i(1, 1)); - if (drag_current_tile != coords && tile_set_atlas_source->can_move_tile_in_atlas(drag_current_tile, coords)) { - tile_set_atlas_source->move_tile_in_atlas(drag_current_tile, coords); - selection.clear(); - selection.insert({ coords, 0 }); - drag_current_tile = coords; + drag_last_mouse_pos = tile_atlas_control->get_local_mouse_position(); + } else if (drag_type == DRAG_TYPE_MOVE_TILE) { + // Move tile. + Vector2 mouse_offset = (Vector2(tile_set_atlas_source->get_tile_size_in_atlas(drag_current_tile)) / 2.0 - Vector2(0.5, 0.5)) * tile_set->get_tile_size(); + Vector2i coords = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position() - mouse_offset); + coords = coords.max(Vector2i(0, 0)).min(grid_size - Vector2i(1, 1)); + if (drag_current_tile != coords && tile_set_atlas_source->can_move_tile_in_atlas(drag_current_tile, coords)) { + tile_set_atlas_source->move_tile_in_atlas(drag_current_tile, coords); + selection.clear(); + selection.insert({ coords, 0 }); + drag_current_tile = coords; - // Update only what's needed. - tile_set_atlas_source_changed_needs_update = false; - _update_tile_inspector(); - _update_atlas_view(); - _update_tile_id_label(); - } - } else if (drag_type >= DRAG_TYPE_RESIZE_TOP_LEFT && drag_type <= DRAG_TYPE_RESIZE_LEFT) { - // Resizing a tile. - new_base_tiles_coords = new_base_tiles_coords.max(Vector2i(-1, -1)).min(grid_size); + // Update only what's needed. + tile_set_atlas_source_changed_needs_update = false; + _update_tile_inspector(); + _update_atlas_view(); + _update_tile_id_label(); + _update_current_tile_data_editor(); + } + } else if (drag_type == DRAG_TYPE_MAY_POPUP_MENU) { + if (Vector2(drag_start_mouse_pos).distance_to(tile_atlas_control->get_local_mouse_position()) > 5.0 * EDSCALE) { + drag_type = DRAG_TYPE_NONE; + } + } else if (drag_type >= DRAG_TYPE_RESIZE_TOP_LEFT && drag_type <= DRAG_TYPE_RESIZE_LEFT) { + // Resizing a tile. + new_base_tiles_coords = new_base_tiles_coords.max(Vector2i(-1, -1)).min(grid_size); - Rect2i old_rect = Rect2i(drag_current_tile, tile_set_atlas_source->get_tile_size_in_atlas(drag_current_tile)); - Rect2i new_rect = old_rect; + Rect2i old_rect = Rect2i(drag_current_tile, tile_set_atlas_source->get_tile_size_in_atlas(drag_current_tile)); + Rect2i new_rect = old_rect; - if (drag_type == DRAG_TYPE_RESIZE_LEFT || drag_type == DRAG_TYPE_RESIZE_TOP_LEFT || drag_type == DRAG_TYPE_RESIZE_BOTTOM_LEFT) { - new_rect.position.x = MIN(new_base_tiles_coords.x + 1, old_rect.get_end().x - 1); - new_rect.size.x = old_rect.get_end().x - new_rect.position.x; - } - if (drag_type == DRAG_TYPE_RESIZE_TOP || drag_type == DRAG_TYPE_RESIZE_TOP_LEFT || drag_type == DRAG_TYPE_RESIZE_TOP_RIGHT) { - new_rect.position.y = MIN(new_base_tiles_coords.y + 1, old_rect.get_end().y - 1); - new_rect.size.y = old_rect.get_end().y - new_rect.position.y; - } + if (drag_type == DRAG_TYPE_RESIZE_LEFT || drag_type == DRAG_TYPE_RESIZE_TOP_LEFT || drag_type == DRAG_TYPE_RESIZE_BOTTOM_LEFT) { + new_rect.position.x = MIN(new_base_tiles_coords.x + 1, old_rect.get_end().x - 1); + new_rect.size.x = old_rect.get_end().x - new_rect.position.x; + } + if (drag_type == DRAG_TYPE_RESIZE_TOP || drag_type == DRAG_TYPE_RESIZE_TOP_LEFT || drag_type == DRAG_TYPE_RESIZE_TOP_RIGHT) { + new_rect.position.y = MIN(new_base_tiles_coords.y + 1, old_rect.get_end().y - 1); + new_rect.size.y = old_rect.get_end().y - new_rect.position.y; + } - if (drag_type == DRAG_TYPE_RESIZE_RIGHT || drag_type == DRAG_TYPE_RESIZE_TOP_RIGHT || drag_type == DRAG_TYPE_RESIZE_BOTTOM_RIGHT) { - new_rect.set_end(Vector2i(MAX(new_base_tiles_coords.x, old_rect.position.x + 1), new_rect.get_end().y)); - } - if (drag_type == DRAG_TYPE_RESIZE_BOTTOM || drag_type == DRAG_TYPE_RESIZE_BOTTOM_LEFT || drag_type == DRAG_TYPE_RESIZE_BOTTOM_RIGHT) { - new_rect.set_end(Vector2i(new_rect.get_end().x, MAX(new_base_tiles_coords.y, old_rect.position.y + 1))); - } + if (drag_type == DRAG_TYPE_RESIZE_RIGHT || drag_type == DRAG_TYPE_RESIZE_TOP_RIGHT || drag_type == DRAG_TYPE_RESIZE_BOTTOM_RIGHT) { + new_rect.set_end(Vector2i(MAX(new_base_tiles_coords.x, old_rect.position.x + 1), new_rect.get_end().y)); + } + if (drag_type == DRAG_TYPE_RESIZE_BOTTOM || drag_type == DRAG_TYPE_RESIZE_BOTTOM_LEFT || drag_type == DRAG_TYPE_RESIZE_BOTTOM_RIGHT) { + new_rect.set_end(Vector2i(new_rect.get_end().x, MAX(new_base_tiles_coords.y, old_rect.position.y + 1))); + } - if (tile_set_atlas_source->can_move_tile_in_atlas(drag_current_tile, new_rect.position, new_rect.size)) { - tile_set_atlas_source->move_tile_in_atlas(drag_current_tile, new_rect.position, new_rect.size); - selection.clear(); - selection.insert({ new_rect.position, 0 }); - drag_current_tile = new_rect.position; + if (tile_set_atlas_source->can_move_tile_in_atlas(drag_current_tile, new_rect.position, new_rect.size)) { + tile_set_atlas_source->move_tile_in_atlas(drag_current_tile, new_rect.position, new_rect.size); + selection.clear(); + selection.insert({ new_rect.position, 0 }); + drag_current_tile = new_rect.position; - // Update only what's needed. - tile_set_atlas_source_changed_needs_update = false; - _update_tile_inspector(); - _update_atlas_view(); - _update_tile_id_label(); + // Update only what's needed. + tile_set_atlas_source_changed_needs_update = false; + _update_tile_inspector(); + _update_atlas_view(); + _update_tile_id_label(); + _update_current_tile_data_editor(); + } } + + // Redraw for the hovered tile. + tile_atlas_control->update(); + tile_atlas_control_unscaled->update(); + alternative_tiles_control->update(); + alternative_tiles_control_unscaled->update(); + tile_atlas_view->update(); + return; } - // Redraw for the hovered tile. - tile_atlas_control->update(); - tile_atlas_control_unscaled->update(); - alternative_tiles_control->update(); - alternative_tiles_control_unscaled->update(); - tile_atlas_view->update(); - return; - } + Ref mb = p_event; + if (mb.is_valid()) { + Vector2 mouse_local_pos = tile_atlas_control->get_local_mouse_position(); + if (mb->get_button_index() == MOUSE_BUTTON_LEFT) { + if (mb->is_pressed()) { + // Left click pressed. + if (tools_button_group->get_pressed_button() == tool_setup_atlas_source_button) { + if (tools_settings_erase_button->is_pressed()) { + // Erasing + if (mb->is_ctrl_pressed() || mb->is_shift_pressed()) { + // Remove tiles using rect. + + // Setup the dragging info. + drag_type = DRAG_TYPE_REMOVE_TILES_USING_RECT; + drag_start_mouse_pos = mouse_local_pos; + drag_last_mouse_pos = drag_start_mouse_pos; + } else { + // Remove tiles. - Ref mb = p_event; - if (mb.is_valid()) { - Vector2 mouse_local_pos = tile_atlas_control->get_local_mouse_position(); - if (mb->get_button_index() == MOUSE_BUTTON_LEFT) { - if (mb->is_pressed()) { - // Left click pressed. - if (tools_button_group->get_pressed_button() == tool_add_remove_button) { - if (tools_settings_erase_button->is_pressed()) { - // Remove tiles. - - // Setup the dragging info. - drag_type = DRAG_TYPE_REMOVE_TILES; - drag_start_mouse_pos = mouse_local_pos; - drag_last_mouse_pos = drag_start_mouse_pos; - - // Remove a first tile. - Vector2i coords = tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_mouse_pos); - if (coords != TileSetSource::INVALID_ATLAS_COORDS) { - coords = tile_set_atlas_source->get_tile_at_coords(coords); - } - if (coords != TileSetSource::INVALID_ATLAS_COORDS) { - drag_modified_tiles.insert(coords); - } - } else { - if (mb->is_shift_pressed()) { - // Create a big tile. - Vector2i coords = tile_atlas_view->get_atlas_tile_coords_at_pos(mouse_local_pos); - if (coords != TileSetSource::INVALID_ATLAS_COORDS && tile_set_atlas_source->get_tile_at_coords(coords) == TileSetSource::INVALID_ATLAS_COORDS) { - // Setup the dragging info, only if we start on an empty tile. - drag_type = DRAG_TYPE_CREATE_BIG_TILE; + // Setup the dragging info. + drag_type = DRAG_TYPE_REMOVE_TILES; drag_start_mouse_pos = mouse_local_pos; drag_last_mouse_pos = drag_start_mouse_pos; - drag_current_tile = coords; - // Create a tile. - tile_set_atlas_source->create_tile(coords); + // Remove a first tile. + Vector2i coords = tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_mouse_pos); + if (coords != TileSetSource::INVALID_ATLAS_COORDS) { + coords = tile_set_atlas_source->get_tile_at_coords(coords); + } + if (coords != TileSetSource::INVALID_ATLAS_COORDS) { + drag_modified_tiles.insert(coords); + } } } else { - // Create tiles. - - // Setup the dragging info. - drag_type = DRAG_TYPE_CREATE_TILES; - drag_start_mouse_pos = mouse_local_pos; - drag_last_mouse_pos = drag_start_mouse_pos; - - // Create a first tile if needed. - Vector2i coords = tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_mouse_pos); - if (coords != TileSetSource::INVALID_ATLAS_COORDS && tile_set_atlas_source->get_tile_at_coords(coords) == TileSetSource::INVALID_ATLAS_COORDS) { - tile_set_atlas_source->create_tile(coords); - drag_modified_tiles.insert(coords); - } - } - } - } else if (tools_button_group->get_pressed_button() == tool_add_remove_rect_button) { - if (tools_settings_erase_button->is_pressed()) { - // Remove tiles using rect. - - // Setup the dragging info. - drag_type = DRAG_TYPE_REMOVE_TILES_USING_RECT; - drag_start_mouse_pos = mouse_local_pos; - drag_last_mouse_pos = drag_start_mouse_pos; - } else { - if (mb->is_shift_pressed()) { - // Create a big tile. - Vector2i coords = tile_atlas_view->get_atlas_tile_coords_at_pos(mouse_local_pos); - if (coords != TileSetSource::INVALID_ATLAS_COORDS && tile_set_atlas_source->get_tile_at_coords(coords) == TileSetSource::INVALID_ATLAS_COORDS) { - // Setup the dragging info, only if we start on an empty tile. - drag_type = DRAG_TYPE_CREATE_BIG_TILE; + // Creating + if (mb->is_shift_pressed()) { + // Create a big tile. + Vector2i coords = tile_atlas_view->get_atlas_tile_coords_at_pos(mouse_local_pos); + if (coords != TileSetSource::INVALID_ATLAS_COORDS && tile_set_atlas_source->get_tile_at_coords(coords) == TileSetSource::INVALID_ATLAS_COORDS) { + // Setup the dragging info, only if we start on an empty tile. + drag_type = DRAG_TYPE_CREATE_BIG_TILE; + drag_start_mouse_pos = mouse_local_pos; + drag_last_mouse_pos = drag_start_mouse_pos; + drag_current_tile = coords; + + // Create a tile. + tile_set_atlas_source->create_tile(coords); + } + } else if (mb->is_ctrl_pressed()) { + // Create tiles using rect. + drag_type = DRAG_TYPE_CREATE_TILES_USING_RECT; drag_start_mouse_pos = mouse_local_pos; drag_last_mouse_pos = drag_start_mouse_pos; - drag_current_tile = coords; + } else { + // Create tiles. - // Create a tile. - tile_set_atlas_source->create_tile(coords); + // Setup the dragging info. + drag_type = DRAG_TYPE_CREATE_TILES; + drag_start_mouse_pos = mouse_local_pos; + drag_last_mouse_pos = drag_start_mouse_pos; + + // Create a first tile if needed. + Vector2i coords = tile_atlas_view->get_atlas_tile_coords_at_pos(drag_start_mouse_pos); + if (coords != TileSetSource::INVALID_ATLAS_COORDS && tile_set_atlas_source->get_tile_at_coords(coords) == TileSetSource::INVALID_ATLAS_COORDS) { + tile_set_atlas_source->create_tile(coords); + drag_modified_tiles.insert(coords); + } } - } else { - // Create tiles using rect. - drag_type = DRAG_TYPE_CREATE_TILES_USING_RECT; - drag_start_mouse_pos = mouse_local_pos; - drag_last_mouse_pos = drag_start_mouse_pos; } - } - } else if (tools_button_group->get_pressed_button() == tool_select_button) { - // Dragging a handle. - drag_type = DRAG_TYPE_NONE; - if (selection.size() == 1) { - TileSelection selected = selection.front()->get(); - if (selected.tile != TileSetSource::INVALID_ATLAS_COORDS && selected.alternative == 0) { - Vector2i size_in_atlas = tile_set_atlas_source->get_tile_size_in_atlas(selected.tile); - Rect2 region = tile_set_atlas_source->get_tile_texture_region(selected.tile); - Size2 zoomed_size = resize_handle->get_size() / tile_atlas_view->get_zoom(); - Rect2 rect = region.grow_individual(zoomed_size.x, zoomed_size.y, 0, 0); - const Vector2i coords[] = { Vector2i(0, 0), Vector2i(1, 0), Vector2i(1, 1), Vector2i(0, 1) }; - const Vector2i directions[] = { Vector2i(0, -1), Vector2i(1, 0), Vector2i(0, 1), Vector2i(-1, 0) }; - CursorShape cursor_shape = CURSOR_ARROW; - bool can_grow[4]; - for (int i = 0; i < 4; i++) { - can_grow[i] = tile_set_atlas_source->can_move_tile_in_atlas(selected.tile, selected.tile + directions[i]); - can_grow[i] |= (i % 2 == 0) ? size_in_atlas.y > 1 : size_in_atlas.x > 1; - } - for (int i = 0; i < 4; i++) { - Vector2 pos = rect.position + Vector2(rect.size.x, rect.size.y) * coords[i]; - if (can_grow[i] && can_grow[(i + 3) % 4] && Rect2(pos, zoomed_size).has_point(mouse_local_pos)) { - drag_type = (DragType)((int)DRAG_TYPE_RESIZE_TOP_LEFT + i * 2); - drag_start_mouse_pos = mouse_local_pos; - drag_last_mouse_pos = drag_start_mouse_pos; - drag_current_tile = selected.tile; - drag_start_tile_shape = Rect2i(selected.tile, tile_set_atlas_source->get_tile_size_in_atlas(selected.tile)); - cursor_shape = (i % 2) ? CURSOR_BDIAGSIZE : CURSOR_FDIAGSIZE; + } else if (tools_button_group->get_pressed_button() == tool_select_button) { + // Dragging a handle. + drag_type = DRAG_TYPE_NONE; + if (selection.size() == 1) { + TileSelection selected = selection.front()->get(); + if (selected.tile != TileSetSource::INVALID_ATLAS_COORDS && selected.alternative == 0) { + Vector2i size_in_atlas = tile_set_atlas_source->get_tile_size_in_atlas(selected.tile); + Rect2 region = tile_set_atlas_source->get_tile_texture_region(selected.tile); + Size2 zoomed_size = resize_handle->get_size() / tile_atlas_view->get_zoom(); + Rect2 rect = region.grow_individual(zoomed_size.x, zoomed_size.y, 0, 0); + const Vector2i coords[] = { Vector2i(0, 0), Vector2i(1, 0), Vector2i(1, 1), Vector2i(0, 1) }; + const Vector2i directions[] = { Vector2i(0, -1), Vector2i(1, 0), Vector2i(0, 1), Vector2i(-1, 0) }; + CursorShape cursor_shape = CURSOR_ARROW; + bool can_grow[4]; + for (int i = 0; i < 4; i++) { + can_grow[i] = tile_set_atlas_source->can_move_tile_in_atlas(selected.tile, selected.tile + directions[i]); + can_grow[i] |= (i % 2 == 0) ? size_in_atlas.y > 1 : size_in_atlas.x > 1; } - Vector2 next_pos = rect.position + Vector2(rect.size.x, rect.size.y) * coords[(i + 1) % 4]; - if (can_grow[i] && Rect2((pos + next_pos) / 2.0, zoomed_size).has_point(mouse_local_pos)) { - drag_type = (DragType)((int)DRAG_TYPE_RESIZE_TOP + i * 2); - drag_start_mouse_pos = mouse_local_pos; - drag_last_mouse_pos = drag_start_mouse_pos; - drag_current_tile = selected.tile; - drag_start_tile_shape = Rect2i(selected.tile, tile_set_atlas_source->get_tile_size_in_atlas(selected.tile)); - cursor_shape = (i % 2) ? CURSOR_HSIZE : CURSOR_VSIZE; + for (int i = 0; i < 4; i++) { + Vector2 pos = rect.position + Vector2(rect.size.x, rect.size.y) * coords[i]; + if (can_grow[i] && can_grow[(i + 3) % 4] && Rect2(pos, zoomed_size).has_point(mouse_local_pos)) { + drag_type = (DragType)((int)DRAG_TYPE_RESIZE_TOP_LEFT + i * 2); + drag_start_mouse_pos = mouse_local_pos; + drag_last_mouse_pos = drag_start_mouse_pos; + drag_current_tile = selected.tile; + drag_start_tile_shape = Rect2i(selected.tile, tile_set_atlas_source->get_tile_size_in_atlas(selected.tile)); + cursor_shape = (i % 2) ? CURSOR_BDIAGSIZE : CURSOR_FDIAGSIZE; + } + Vector2 next_pos = rect.position + Vector2(rect.size.x, rect.size.y) * coords[(i + 1) % 4]; + if (can_grow[i] && Rect2((pos + next_pos) / 2.0, zoomed_size).has_point(mouse_local_pos)) { + drag_type = (DragType)((int)DRAG_TYPE_RESIZE_TOP + i * 2); + drag_start_mouse_pos = mouse_local_pos; + drag_last_mouse_pos = drag_start_mouse_pos; + drag_current_tile = selected.tile; + drag_start_tile_shape = Rect2i(selected.tile, tile_set_atlas_source->get_tile_size_in_atlas(selected.tile)); + cursor_shape = (i % 2) ? CURSOR_HSIZE : CURSOR_VSIZE; + } } + tile_atlas_control->set_default_cursor_shape(cursor_shape); } - tile_atlas_control->set_default_cursor_shape(cursor_shape); } - } - // Selecting then dragging a tile. - if (drag_type == DRAG_TYPE_NONE) { - TileSelection selected = { TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE }; - Vector2i coords = tile_atlas_view->get_atlas_tile_coords_at_pos(mouse_local_pos); - if (coords != TileSetSource::INVALID_ATLAS_COORDS) { - coords = tile_set_atlas_source->get_tile_at_coords(coords); + // Selecting then dragging a tile. + if (drag_type == DRAG_TYPE_NONE) { + TileSelection selected = { TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE }; + Vector2i coords = tile_atlas_view->get_atlas_tile_coords_at_pos(mouse_local_pos); if (coords != TileSetSource::INVALID_ATLAS_COORDS) { - selected = { coords, 0 }; + coords = tile_set_atlas_source->get_tile_at_coords(coords); + if (coords != TileSetSource::INVALID_ATLAS_COORDS) { + selected = { coords, 0 }; + } } - } - bool shift = mb->is_shift_pressed(); - if (!shift && selection.size() == 1 && selected.tile != TileSetSource::INVALID_ATLAS_COORDS && selection.has(selected)) { - // Start move dragging. - drag_type = DRAG_TYPE_MOVE_TILE; - drag_start_mouse_pos = mouse_local_pos; - drag_last_mouse_pos = drag_start_mouse_pos; - drag_current_tile = selected.tile; - drag_start_tile_shape = Rect2i(selected.tile, tile_set_atlas_source->get_tile_size_in_atlas(selected.tile)); - tile_atlas_control->set_default_cursor_shape(CURSOR_MOVE); - } else { - // Start selection dragging. - drag_type = DRAG_TYPE_RECT_SELECT; - drag_start_mouse_pos = mouse_local_pos; - drag_last_mouse_pos = drag_start_mouse_pos; + bool shift = mb->is_shift_pressed(); + if (!shift && selection.size() == 1 && selected.tile != TileSetSource::INVALID_ATLAS_COORDS && selection.has(selected)) { + // Start move dragging. + drag_type = DRAG_TYPE_MOVE_TILE; + drag_start_mouse_pos = mouse_local_pos; + drag_last_mouse_pos = drag_start_mouse_pos; + drag_current_tile = selected.tile; + drag_start_tile_shape = Rect2i(selected.tile, tile_set_atlas_source->get_tile_size_in_atlas(selected.tile)); + tile_atlas_control->set_default_cursor_shape(CURSOR_MOVE); + } else { + // Start selection dragging. + drag_type = DRAG_TYPE_RECT_SELECT; + drag_start_mouse_pos = mouse_local_pos; + drag_last_mouse_pos = drag_start_mouse_pos; + } } } + } else { + // Left click released. + _end_dragging(); } - } else { - // Left click released. - _end_dragging(); - } - tile_atlas_control->update(); - tile_atlas_control_unscaled->update(); - alternative_tiles_control->update(); - alternative_tiles_control_unscaled->update(); - tile_atlas_view->update(); - return; - } else if (mb->get_button_index() == MOUSE_BUTTON_RIGHT) { - if (mb->is_pressed()) { + tile_atlas_control->update(); + tile_atlas_control_unscaled->update(); + alternative_tiles_control->update(); + alternative_tiles_control_unscaled->update(); + tile_atlas_view->update(); + return; + } else if (mb->get_button_index() == MOUSE_BUTTON_RIGHT) { // Right click pressed. - - TileSelection selected = { tile_atlas_view->get_atlas_tile_coords_at_pos(mouse_local_pos), 0 }; - if (selected.tile != TileSetSource::INVALID_ATLAS_COORDS) { - selected.tile = tile_set_atlas_source->get_tile_at_coords(selected.tile); - } - - // Set the selection if needed. - if (selection.size() <= 1) { - if (selected.tile != TileSetSource::INVALID_ATLAS_COORDS) { - undo_redo->create_action(TTR("Select tiles")); - undo_redo->add_undo_method(this, "_set_selection_from_array", _get_selection_as_array()); - selection.clear(); - selection.insert(selected); - undo_redo->add_do_method(this, "_set_selection_from_array", _get_selection_as_array()); - undo_redo->commit_action(false); - _update_tile_inspector(); - _update_tile_id_label(); - } - } - - // Pops up the correct menu, depending on whether we have a tile or not. - if (selected.tile != TileSetSource::INVALID_ATLAS_COORDS && selection.has(selected)) { - // We have a tile. - menu_option_coords = selected.tile; - menu_option_alternative = 0; - base_tile_popup_menu->popup(Rect2i(get_global_mouse_position(), Size2i())); - } else if (hovered_base_tile_coords != TileSetSource::INVALID_ATLAS_COORDS) { - // We don't have a tile, but can create one. - menu_option_coords = hovered_base_tile_coords; - menu_option_alternative = TileSetSource::INVALID_TILE_ALTERNATIVE; - empty_base_tile_popup_menu->popup(Rect2i(get_global_mouse_position(), Size2i())); + if (mb->is_pressed()) { + drag_type = DRAG_TYPE_MAY_POPUP_MENU; + drag_start_mouse_pos = tile_atlas_control->get_local_mouse_position(); + } else { + // Right click released. + _end_dragging(); } - } else { - // Right click released. - _end_dragging(); + tile_atlas_control->update(); + tile_atlas_control_unscaled->update(); + alternative_tiles_control->update(); + alternative_tiles_control_unscaled->update(); + tile_atlas_view->update(); + return; } - tile_atlas_control->update(); - tile_atlas_control_unscaled->update(); - alternative_tiles_control->update(); - alternative_tiles_control_unscaled->update(); - tile_atlas_view->update(); - return; } } } @@ -1000,10 +1287,45 @@ void TileSetAtlasSourceEditor::_end_dragging() { } _update_tile_inspector(); _update_tile_id_label(); + _update_current_tile_data_editor(); undo_redo->add_do_method(this, "_set_selection_from_array", _get_selection_as_array()); undo_redo->commit_action(false); - break; - } + } break; + case DRAG_TYPE_MAY_POPUP_MENU: { + Vector2 mouse_local_pos = tile_atlas_control->get_local_mouse_position(); + TileSelection selected = { tile_atlas_view->get_atlas_tile_coords_at_pos(mouse_local_pos), 0 }; + if (selected.tile != TileSetSource::INVALID_ATLAS_COORDS) { + selected.tile = tile_set_atlas_source->get_tile_at_coords(selected.tile); + } + + // Set the selection if needed. + if (selection.size() <= 1) { + if (selected.tile != TileSetSource::INVALID_ATLAS_COORDS) { + undo_redo->create_action(TTR("Select tiles")); + undo_redo->add_undo_method(this, "_set_selection_from_array", _get_selection_as_array()); + selection.clear(); + selection.insert(selected); + undo_redo->add_do_method(this, "_set_selection_from_array", _get_selection_as_array()); + undo_redo->commit_action(false); + _update_tile_inspector(); + _update_tile_id_label(); + _update_current_tile_data_editor(); + } + } + + // Pops up the correct menu, depending on whether we have a tile or not. + if (selected.tile != TileSetSource::INVALID_ATLAS_COORDS && selection.has(selected)) { + // We have a tile. + menu_option_coords = selected.tile; + menu_option_alternative = 0; + base_tile_popup_menu->popup(Rect2i(get_global_mouse_position(), Size2i())); + } else if (hovered_base_tile_coords != TileSetSource::INVALID_ATLAS_COORDS) { + // We don't have a tile, but can create one. + menu_option_coords = hovered_base_tile_coords; + menu_option_alternative = TileSetSource::INVALID_TILE_ALTERNATIVE; + empty_base_tile_popup_menu->popup(Rect2i(get_global_mouse_position(), Size2i())); + } + } break; case DRAG_TYPE_RESIZE_TOP_LEFT: case DRAG_TYPE_RESIZE_TOP: case DRAG_TYPE_RESIZE_TOP_RIGHT: @@ -1166,6 +1488,7 @@ void TileSetAtlasSourceEditor::_set_selection_from_array(Array p_selection) { _update_tile_inspector(); _update_tile_id_label(); _update_atlas_view(); + _update_current_tile_data_editor(); } Array TileSetAtlasSourceEditor::_get_selection_as_array() { @@ -1297,7 +1620,7 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_draw() { tile_atlas_control->draw_rect(tile_set_atlas_source->get_tile_texture_region(hovered_tile), Color(1.0, 1.0, 1.0), false); } else { // Draw empty tile, only in add/remove tiles mode. - if (tools_button_group->get_pressed_button() == tool_add_remove_button || tools_button_group->get_pressed_button() == tool_add_remove_rect_button) { + if (tools_button_group->get_pressed_button() == tool_setup_atlas_source_button) { Vector2i margins = tile_set_atlas_source->get_margins(); Vector2i separation = tile_set_atlas_source->get_separation(); Vector2i tile_size = tile_set_atlas_source->get_texture_region_size(); @@ -1310,9 +1633,8 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_draw() { } void TileSetAtlasSourceEditor::_tile_atlas_control_unscaled_draw() { - // Draw the preview of the selected property. - TileDataEditor *tile_data_editor = TileSetEditor::get_singleton()->get_tile_data_editor(selected_property); - if (tile_data_editor && tile_inspector->is_visible_in_tree()) { + if (current_tile_data_editor) { + // Draw the preview of the selected property. for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) { Vector2i coords = tile_set_atlas_source->get_tile_id(i); Rect2i texture_region = tile_set_atlas_source->get_tile_texture_region(coords); @@ -1321,7 +1643,41 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_unscaled_draw() { Transform2D xform = tile_atlas_control->get_parent_control()->get_transform(); xform.translate(position); - tile_data_editor->draw_over_tile(tile_atlas_control_unscaled, xform, *tile_set, tile_set_atlas_source_id, coords, 0, selected_property); + if (tools_button_group->get_pressed_button() == tool_select_button && selection.has({ coords, 0 })) { + continue; + } + + TileMapCell cell; + cell.source_id = tile_set_atlas_source_id; + cell.set_atlas_coords(coords); + cell.alternative_tile = 0; + current_tile_data_editor->draw_over_tile(tile_atlas_control_unscaled, xform, cell); + } + + // Draw the selection on top of other. + if (tools_button_group->get_pressed_button() == tool_select_button) { + for (Set::Element *E = selection.front(); E; E = E->next()) { + if (E->get().alternative != 0) { + continue; + } + Rect2i texture_region = tile_set_atlas_source->get_tile_texture_region(E->get().tile); + Vector2i position = (texture_region.position + texture_region.get_end()) / 2 + tile_set_atlas_source->get_tile_effective_texture_offset(E->get().tile, 0); + + Transform2D xform = tile_atlas_control->get_parent_control()->get_transform(); + xform.translate(position); + + TileMapCell cell; + cell.source_id = tile_set_atlas_source_id; + cell.set_atlas_coords(E->get().tile); + cell.alternative_tile = 0; + current_tile_data_editor->draw_over_tile(tile_atlas_control_unscaled, xform, cell, true); + } + } + + // Call the TileData's editor custom draw function. + if (tools_button_group->get_pressed_button() == tool_paint_button) { + Transform2D xform = tile_atlas_control->get_parent_control()->get_transform(); + current_tile_data_editor->forward_draw_over_atlas(tile_atlas_view, tile_set_atlas_source, tile_atlas_control_unscaled, xform); } } } @@ -1330,6 +1686,19 @@ void TileSetAtlasSourceEditor::_tile_alternatives_control_gui_input(const Refget_alternative_tile_at_pos(alternative_tiles_control->get_local_mouse_position()); + // Forward the event to the current tile data editor if we are in the painting mode. + if (tools_button_group->get_pressed_button() == tool_paint_button) { + if (current_tile_data_editor) { + current_tile_data_editor->forward_painting_alternatives_gui_input(tile_atlas_view, tile_set_atlas_source, p_event); + } + tile_atlas_control->update(); + tile_atlas_control_unscaled->update(); + alternative_tiles_control->update(); + alternative_tiles_control_unscaled->update(); + tile_atlas_view->update(); + return; + } + Ref mm = p_event; if (mm.is_valid()) { tile_atlas_control->update(); @@ -1425,7 +1794,60 @@ void TileSetAtlasSourceEditor::_tile_alternatives_control_draw() { } void TileSetAtlasSourceEditor::_tile_alternatives_control_unscaled_draw() { - //TODO + // Draw the preview of the selected property. + if (current_tile_data_editor) { + // Draw the preview of the currently selected property. + for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) { + Vector2i coords = tile_set_atlas_source->get_tile_id(i); + for (int j = 0; j < tile_set_atlas_source->get_alternative_tiles_count(coords); j++) { + int alternative_tile = tile_set_atlas_source->get_alternative_tile_id(coords, j); + if (alternative_tile == 0) { + continue; + } + Rect2i rect = tile_atlas_view->get_alternative_tile_rect(coords, alternative_tile); + Vector2 position = (rect.get_position() + rect.get_end()) / 2; + + Transform2D xform = alternative_tiles_control->get_parent_control()->get_transform(); + xform.translate(position); + + if (tools_button_group->get_pressed_button() == tool_select_button && selection.has({ coords, alternative_tile })) { + continue; + } + + TileMapCell cell; + cell.source_id = tile_set_atlas_source_id; + cell.set_atlas_coords(coords); + cell.alternative_tile = alternative_tile; + current_tile_data_editor->draw_over_tile(alternative_tiles_control_unscaled, xform, cell); + } + } + + // Draw the selection on top of other. + if (tools_button_group->get_pressed_button() == tool_select_button) { + for (Set::Element *E = selection.front(); E; E = E->next()) { + if (E->get().alternative == 0) { + continue; + } + Rect2i rect = tile_atlas_view->get_alternative_tile_rect(E->get().tile, E->get().alternative); + Vector2 position = (rect.get_position() + rect.get_end()) / 2; + + Transform2D xform = alternative_tiles_control->get_parent_control()->get_transform(); + xform.translate(position); + + TileMapCell cell; + cell.source_id = tile_set_atlas_source_id; + cell.set_atlas_coords(E->get().tile); + cell.alternative_tile = E->get().alternative; + current_tile_data_editor->draw_over_tile(alternative_tiles_control_unscaled, xform, cell, true); + } + } + + // Call the TileData's editor custom draw function. + if (tools_button_group->get_pressed_button() == tool_paint_button) { + Transform2D xform = tile_atlas_control->get_parent_control()->get_transform(); + current_tile_data_editor->forward_draw_over_alternatives(tile_atlas_view, tile_set_atlas_source, alternative_tiles_control_unscaled, xform); + } + } } void TileSetAtlasSourceEditor::_tile_set_atlas_source_changed() { @@ -1449,15 +1871,23 @@ void TileSetAtlasSourceEditor::_undo_redo_inspector_callback(Object *p_undo_redo AtlasTileProxyObject *tile_data = Object::cast_to(p_edited); if (tile_data) { Vector components = String(p_property).split("/", true, 2); - if (components.size() == 2 && components[1] == "shapes_count") { + if (components.size() == 2 && components[1] == "polygons_count") { int layer_index = components[0].trim_prefix("physics_layer_").to_int(); - int new_shapes_count = p_new_value; - int old_shapes_count = tile_data->get(vformat("physics_layer_%d/shapes_count", layer_index)); - if (new_shapes_count < old_shapes_count) { - for (int i = new_shapes_count - 1; i < old_shapes_count; i++) { - ADD_UNDO(tile_data, vformat("physics_layer_%d/shape_%d/shape", layer_index, i)); - ADD_UNDO(tile_data, vformat("physics_layer_%d/shape_%d/one_way", layer_index, i)); - ADD_UNDO(tile_data, vformat("physics_layer_%d/shape_%d/one_way_margin", layer_index, i)); + int new_polygons_count = p_new_value; + int old_polygons_count = tile_data->get(vformat("physics_layer_%d/polygons_count", layer_index)); + if (new_polygons_count < old_polygons_count) { + for (int i = new_polygons_count - 1; i < old_polygons_count; i++) { + ADD_UNDO(tile_data, vformat("physics_layer_%d/polygon_%d/points", layer_index, i)); + ADD_UNDO(tile_data, vformat("physics_layer_%d/polygon_%d/one_way", layer_index, i)); + ADD_UNDO(tile_data, vformat("physics_layer_%d/polygon_%d/one_way_margin", layer_index, i)); + } + } + } else if (p_property == "terrain_set") { + int current_terrain_set = tile_data->get("terrain_set"); + for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { + TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); + if (tile_set->is_valid_peering_bit_terrain(current_terrain_set, bit)) { + ADD_UNDO(tile_data, "terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i])); } } } @@ -1500,7 +1930,10 @@ void TileSetAtlasSourceEditor::edit(Ref p_tile_set, TileSetAtlasSource _update_fix_selected_and_hovered_tiles(); _update_tile_id_label(); _update_atlas_view(); + _update_atlas_source_inspector(); _update_tile_inspector(); + _update_tile_data_editors(); + _update_current_tile_data_editor(); } void TileSetAtlasSourceEditor::init_source() { @@ -1616,13 +2049,13 @@ void TileSetAtlasSourceEditor::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: + tool_setup_atlas_source_button->set_icon(get_theme_icon("Tools", "EditorIcons")); tool_select_button->set_icon(get_theme_icon("ToolSelect", "EditorIcons")); - tool_add_remove_button->set_icon(get_theme_icon("EditAddRemove", "EditorIcons")); - tool_add_remove_rect_button->set_icon(get_theme_icon("RectangleAddRemove", "EditorIcons")); + tool_paint_button->set_icon(get_theme_icon("CanvasItem", "EditorIcons")); tools_settings_erase_button->set_icon(get_theme_icon("Eraser", "EditorIcons")); - tool_advanced_menu_buttom->set_icon(get_theme_icon("Tools", "EditorIcons")); + tool_advanced_menu_buttom->set_icon(get_theme_icon("GuiTabMenu", "EditorIcons")); resize_handle = get_theme_icon("EditorHandle", "EditorIcons"); resize_handle_disabled = get_theme_icon("EditorHandleDisabled", "EditorIcons"); @@ -1636,7 +2069,10 @@ void TileSetAtlasSourceEditor::_notification(int p_what) { _update_fix_selected_and_hovered_tiles(); _update_tile_id_label(); _update_atlas_view(); + _update_atlas_source_inspector(); _update_tile_inspector(); + _update_tile_data_editors(); + _update_current_tile_data_editor(); tile_set_atlas_source_changed_needs_update = false; } @@ -1675,7 +2111,6 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() { // Tile inspector. tile_inspector_label = memnew(Label); tile_inspector_label->set_text(TTR("Tile Properties:")); - tile_inspector_label->hide(); middle_vbox_container->add_child(tile_inspector_label); tile_proxy_object = memnew(AtlasTileProxyObject(this)); @@ -1689,6 +2124,36 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() { tile_inspector->connect("property_selected", callable_mp(this, &TileSetAtlasSourceEditor::_inspector_property_selected)); middle_vbox_container->add_child(tile_inspector); + tile_inspector_no_tile_selected_label = memnew(Label); + tile_inspector_no_tile_selected_label->set_align(Label::ALIGN_CENTER); + tile_inspector_no_tile_selected_label->set_text(TTR("No tile selected.")); + middle_vbox_container->add_child(tile_inspector_no_tile_selected_label); + + // Property values palette. + tile_data_editors_popup = memnew(Popup); + + tile_data_editors_label = memnew(Label); + tile_data_editors_label->set_text(TTR("Paint Properties:")); + middle_vbox_container->add_child(tile_data_editors_label); + + tile_data_editor_dropdown_button = memnew(Button); + tile_data_editor_dropdown_button->connect("draw", callable_mp(this, &TileSetAtlasSourceEditor::_tile_data_editor_dropdown_button_draw)); + tile_data_editor_dropdown_button->connect("pressed", callable_mp(this, &TileSetAtlasSourceEditor::_tile_data_editor_dropdown_button_pressed)); + middle_vbox_container->add_child(tile_data_editor_dropdown_button); + tile_data_editor_dropdown_button->add_child(tile_data_editors_popup); + + tile_data_editors_tree = memnew(Tree); + tile_data_editors_tree->set_hide_root(true); + tile_data_editors_tree->set_anchors_and_offsets_preset(Control::PRESET_WIDE); + tile_data_editors_tree->set_h_scroll_enabled(false); + tile_data_editors_tree->set_v_scroll_enabled(false); + tile_data_editors_tree->connect("item_selected", callable_mp(this, &TileSetAtlasSourceEditor::_tile_data_editors_tree_selected)); + tile_data_editors_popup->add_child(tile_data_editors_tree); + + tile_data_painting_editor_container = memnew(VBoxContainer); + tile_data_painting_editor_container->set_h_size_flags(SIZE_EXPAND_FILL); + middle_vbox_container->add_child(tile_data_painting_editor_container); + // Atlas source inspector. atlas_source_inspector_label = memnew(Label); atlas_source_inspector_label->set_text(TTR("Atlas Properties:")); @@ -1720,46 +2185,40 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() { // -- Toolbox -- tools_button_group.instantiate(); + tools_button_group->connect("pressed", callable_mp(this, &TileSetAtlasSourceEditor::_update_fix_selected_and_hovered_tiles).unbind(1)); + tools_button_group->connect("pressed", callable_mp(this, &TileSetAtlasSourceEditor::_update_tile_id_label).unbind(1)); + tools_button_group->connect("pressed", callable_mp(this, &TileSetAtlasSourceEditor::_update_atlas_source_inspector).unbind(1)); + tools_button_group->connect("pressed", callable_mp(this, &TileSetAtlasSourceEditor::_update_tile_inspector).unbind(1)); + tools_button_group->connect("pressed", callable_mp(this, &TileSetAtlasSourceEditor::_update_tile_data_editors).unbind(1)); + tools_button_group->connect("pressed", callable_mp(this, &TileSetAtlasSourceEditor::_update_current_tile_data_editor).unbind(1)); + tools_button_group->connect("pressed", callable_mp(this, &TileSetAtlasSourceEditor::_update_atlas_view).unbind(1)); + tools_button_group->connect("pressed", callable_mp(this, &TileSetAtlasSourceEditor::_update_toolbar).unbind(1)); toolbox = memnew(HBoxContainer); right_panel->add_child(toolbox); + tool_setup_atlas_source_button = memnew(Button); + tool_setup_atlas_source_button->set_flat(true); + tool_setup_atlas_source_button->set_toggle_mode(true); + tool_setup_atlas_source_button->set_pressed(true); + tool_setup_atlas_source_button->set_button_group(tools_button_group); + tool_setup_atlas_source_button->set_tooltip(TTR("Atlas Setup. Add/Remove tiles tool (use the shift key to create big tiles, control for rectangle editing).")); + toolbox->add_child(tool_setup_atlas_source_button); + tool_select_button = memnew(Button); tool_select_button->set_flat(true); tool_select_button->set_toggle_mode(true); - tool_select_button->set_pressed(true); + tool_select_button->set_pressed(false); tool_select_button->set_button_group(tools_button_group); tool_select_button->set_tooltip(TTR("Select tiles.")); - tool_select_button->connect("pressed", callable_mp(this, &TileSetAtlasSourceEditor::_update_fix_selected_and_hovered_tiles)); - tool_select_button->connect("pressed", callable_mp(this, &TileSetAtlasSourceEditor::_update_tile_id_label)); - tool_select_button->connect("pressed", callable_mp(this, &TileSetAtlasSourceEditor::_update_tile_inspector)); - tool_select_button->connect("pressed", callable_mp(this, &TileSetAtlasSourceEditor::_update_atlas_view)); - tool_select_button->connect("pressed", callable_mp(this, &TileSetAtlasSourceEditor::_update_toolbar)); toolbox->add_child(tool_select_button); - tool_add_remove_button = memnew(Button); - tool_add_remove_button->set_flat(true); - tool_add_remove_button->set_toggle_mode(true); - tool_add_remove_button->set_button_group(tools_button_group); - tool_add_remove_button->set_tooltip(TTR("Add/Remove tiles tool (use the shift key to create big tiles).")); - tool_add_remove_button->connect("pressed", callable_mp(this, &TileSetAtlasSourceEditor::_update_fix_selected_and_hovered_tiles)); - tool_add_remove_button->connect("pressed", callable_mp(this, &TileSetAtlasSourceEditor::_update_tile_id_label)); - tool_add_remove_button->connect("pressed", callable_mp(this, &TileSetAtlasSourceEditor::_update_tile_inspector)); - tool_add_remove_button->connect("pressed", callable_mp(this, &TileSetAtlasSourceEditor::_update_atlas_view)); - tool_add_remove_button->connect("pressed", callable_mp(this, &TileSetAtlasSourceEditor::_update_toolbar)); - toolbox->add_child(tool_add_remove_button); - - tool_add_remove_rect_button = memnew(Button); - tool_add_remove_rect_button->set_flat(true); - tool_add_remove_rect_button->set_toggle_mode(true); - tool_add_remove_rect_button->set_button_group(tools_button_group); - tool_add_remove_rect_button->set_tooltip(TTR("Add/Remove tiles rectangle tool (use the shift key to create big tiles).")); - tool_add_remove_rect_button->connect("pressed", callable_mp(this, &TileSetAtlasSourceEditor::_update_fix_selected_and_hovered_tiles)); - tool_add_remove_rect_button->connect("pressed", callable_mp(this, &TileSetAtlasSourceEditor::_update_tile_id_label)); - tool_add_remove_rect_button->connect("pressed", callable_mp(this, &TileSetAtlasSourceEditor::_update_tile_inspector)); - tool_add_remove_rect_button->connect("pressed", callable_mp(this, &TileSetAtlasSourceEditor::_update_atlas_view)); - tool_add_remove_rect_button->connect("pressed", callable_mp(this, &TileSetAtlasSourceEditor::_update_toolbar)); - toolbox->add_child(tool_add_remove_rect_button); + tool_paint_button = memnew(Button); + tool_paint_button->set_flat(true); + tool_paint_button->set_toggle_mode(true); + tool_paint_button->set_button_group(tools_button_group); + tool_paint_button->set_tooltip(TTR("Paint properties.")); + toolbox->add_child(tool_paint_button); // Tool settings. tool_settings = memnew(HBoxContainer); @@ -1768,6 +2227,9 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() { tool_settings_vsep = memnew(VSeparator); tool_settings->add_child(tool_settings_vsep); + tool_settings_tile_data_toolbar_container = memnew(HBoxContainer); + tool_settings->add_child(tool_settings_tile_data_toolbar_container); + tools_settings_erase_button = memnew(Button); tools_settings_erase_button->set_flat(true); tools_settings_erase_button->set_toggle_mode(true); @@ -1775,9 +2237,6 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() { tools_settings_erase_button->set_shortcut_context(this); tool_settings->add_child(tools_settings_erase_button); - VSeparator *tool_advanced_vsep = memnew(VSeparator); - toolbox->add_child(tool_advanced_vsep); - tool_advanced_menu_buttom = memnew(MenuButton); tool_advanced_menu_buttom->set_flat(true); tool_advanced_menu_buttom->get_popup()->add_item(TTR("Cleanup Tiles Outside Texture"), ADVANCED_CLEANUP_TILES_OUTSIDE_TEXTURE); @@ -1844,7 +2303,7 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() { alternative_tiles_control_unscaled = memnew(Control); alternative_tiles_control_unscaled->set_anchors_and_offsets_preset(Control::PRESET_WIDE); alternative_tiles_control_unscaled->connect("draw", callable_mp(this, &TileSetAtlasSourceEditor::_tile_alternatives_control_unscaled_draw)); - tile_atlas_view->add_control_over_atlas_tiles(alternative_tiles_control_unscaled, false); + tile_atlas_view->add_control_over_alternative_tiles(alternative_tiles_control_unscaled, false); alternative_tiles_control_unscaled->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); tile_atlas_view_missing_source_label = memnew(Label); diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.h b/editor/plugins/tiles/tile_set_atlas_source_editor.h index 70f2cdbe01..dbb0756a16 100644 --- a/editor/plugins/tiles/tile_set_atlas_source_editor.h +++ b/editor/plugins/tiles/tile_set_atlas_source_editor.h @@ -32,6 +32,7 @@ #define TILE_SET_ATLAS_SOURCE_EDITOR_H #include "tile_atlas_view.h" +#include "tile_data_editors.h" #include "editor/editor_node.h" #include "scene/gui/split_container.h" @@ -113,10 +114,27 @@ private: bool tile_set_atlas_source_changed_needs_update = false; + // -- Properties painting -- + VBoxContainer *tile_data_painting_editor_container; + Label *tile_data_editors_label; + Button *tile_data_editor_dropdown_button; + Popup *tile_data_editors_popup; + Tree *tile_data_editors_tree; + void _tile_data_editor_dropdown_button_draw(); + void _tile_data_editor_dropdown_button_pressed(); + + // -- Tile data editors -- + String current_property; + Control *current_tile_data_editor_toolbar = nullptr; + Map tile_data_editors; + TileDataEditor *current_tile_data_editor = nullptr; + void _tile_data_editors_tree_selected(); + // -- Inspector -- AtlasTileProxyObject *tile_proxy_object; Label *tile_inspector_label; EditorInspector *tile_inspector; + Label *tile_inspector_no_tile_selected_label; String selected_property; void _inspector_property_selected(String p_property); @@ -142,6 +160,8 @@ private: DRAG_TYPE_RECT_SELECT, + DRAG_TYPE_MAY_POPUP_MENU, + // Warning: keep in this order. DRAG_TYPE_RESIZE_TOP_LEFT, DRAG_TYPE_RESIZE_TOP, @@ -179,15 +199,16 @@ private: // Tool buttons. Ref tools_button_group; + Button *tool_setup_atlas_source_button; Button *tool_select_button; - Button *tool_add_remove_button; - Button *tool_add_remove_rect_button; + Button *tool_paint_button; Label *tool_tile_id_label; + // Tool settings. HBoxContainer *tool_settings; VSeparator *tool_settings_vsep; + HBoxContainer *tool_settings_tile_data_toolbar_container; Button *tools_settings_erase_button; - MenuButton *tool_advanced_menu_buttom; // Selection. @@ -226,7 +247,10 @@ private: void _update_tile_id_label(); void _update_source_inspector(); void _update_fix_selected_and_hovered_tiles(); + void _update_atlas_source_inspector(); void _update_tile_inspector(); + void _update_tile_data_editors(); + void _update_current_tile_data_editor(); void _update_manage_tile_properties_button(); void _update_atlas_view(); void _update_toolbar(); diff --git a/editor/plugins/tiles/tile_set_editor.cpp b/editor/plugins/tiles/tile_set_editor.cpp index ae5620a4e3..2c2ebd107f 100644 --- a/editor/plugins/tiles/tile_set_editor.cpp +++ b/editor/plugins/tiles/tile_set_editor.cpp @@ -347,11 +347,11 @@ void TileSetEditor::_undo_redo_inspector_callback(Object *p_undo_redo, Object *p int old_layer_count = tile_set->get_physics_layers_count(); if (new_layer_count < old_layer_count) { for (int physics_layer_index = new_layer_count - 1; physics_layer_index < old_layer_count; physics_layer_index++) { - ADD_UNDO(tile_data, vformat("physics_layer_%d/shapes_count", physics_layer_index)); - for (int shape_index = 0; shape_index < tile_data->get_collision_shapes_count(physics_layer_index); shape_index++) { - ADD_UNDO(tile_data, vformat("physics_layer_%d/shape_%d/shape", physics_layer_index, shape_index)); - ADD_UNDO(tile_data, vformat("physics_layer_%d/shape_%d/one_way", physics_layer_index, shape_index)); - ADD_UNDO(tile_data, vformat("physics_layer_%d/shape_%d/one_way_margin", physics_layer_index, shape_index)); + ADD_UNDO(tile_data, vformat("physics_layer_%d/polygons_count", physics_layer_index)); + for (int polygon_index = 0; polygon_index < tile_data->get_collision_polygons_count(physics_layer_index); polygon_index++) { + ADD_UNDO(tile_data, vformat("physics_layer_%d/polygon_%d/points", physics_layer_index, polygon_index)); + ADD_UNDO(tile_data, vformat("physics_layer_%d/polygon_%d/one_way", physics_layer_index, polygon_index)); + ADD_UNDO(tile_data, vformat("physics_layer_%d/polygon_%d/one_way_margin", physics_layer_index, polygon_index)); } } } @@ -359,53 +359,11 @@ void TileSetEditor::_undo_redo_inspector_callback(Object *p_undo_redo, Object *p (components.size() == 2 && components[0].begins_with("terrain_set_") && components[0].trim_prefix("terrain_set_").is_valid_int() && components[1] == "mode") || (components.size() == 2 && components[0].begins_with("terrain_set_") && components[0].trim_prefix("terrain_set_").is_valid_int() && components[1] == "terrains_count" && tile_data->get_terrain_set() == components[0].trim_prefix("terrain_set_").to_int() && (int)p_new_value < tile_set->get_terrains_count(tile_data->get_terrain_set()))) { ADD_UNDO(tile_data, "terrain_set"); - if (tile_data->is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_RIGHT_SIDE)) { - ADD_UNDO(tile_data, "terrains_peering_bit/right_side"); - } - if (tile_data->is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_RIGHT_CORNER)) { - ADD_UNDO(tile_data, "terrains_peering_bit/right_corner"); - } - if (tile_data->is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)) { - ADD_UNDO(tile_data, "terrains_peering_bit/bottom_right_side"); - } - if (tile_data->is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER)) { - ADD_UNDO(tile_data, "terrains_peering_bit/bottom_right_corner"); - } - if (tile_data->is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)) { - ADD_UNDO(tile_data, "terrains_peering_bit/bottom_side"); - } - if (tile_data->is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_CORNER)) { - ADD_UNDO(tile_data, "terrains_peering_bit/bottom_corner"); - } - if (tile_data->is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)) { - ADD_UNDO(tile_data, "terrains_peering_bit/bottom_left_side"); - } - if (tile_data->is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER)) { - ADD_UNDO(tile_data, "terrains_peering_bit/bottom_left_corner"); - } - if (tile_data->is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_LEFT_SIDE)) { - ADD_UNDO(tile_data, "terrains_peering_bit/left_side"); - } - if (tile_data->is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_LEFT_CORNER)) { - ADD_UNDO(tile_data, "terrains_peering_bit/left_corner"); - } - if (tile_data->is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE)) { - ADD_UNDO(tile_data, "terrains_peering_bit/top_left_side"); - } - if (tile_data->is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER)) { - ADD_UNDO(tile_data, "terrains_peering_bit/top_left_corner"); - } - if (tile_data->is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_SIDE)) { - ADD_UNDO(tile_data, "terrains_peering_bit/top_side"); - } - if (tile_data->is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_CORNER)) { - ADD_UNDO(tile_data, "terrains_peering_bit/top_corner"); - } - if (tile_data->is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE)) { - ADD_UNDO(tile_data, "terrains_peering_bit/top_right_side"); - } - if (tile_data->is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER)) { - ADD_UNDO(tile_data, "terrains_peering_bit/top_right_corner"); + for (int l = 0; l < TileSet::CELL_NEIGHBOR_MAX; l++) { + TileSet::CellNeighbor bit = TileSet::CellNeighbor(l); + if (tile_data->is_valid_peering_bit_terrain(bit)) { + ADD_UNDO(tile_data, "terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[l])); + } } } else if (p_property == "navigation_layers_count") { int new_layer_count = p_new_value; @@ -440,30 +398,6 @@ void TileSetEditor::_bind_methods() { ClassDB::bind_method(D_METHOD("_drop_data_fw"), &TileSetEditor::drop_data_fw); } -TileDataEditor *TileSetEditor::get_tile_data_editor(String p_property) { - Vector components = String(p_property).split("/", true); - - if (p_property == "z_index") { - return tile_data_integer_editor; - } else if (p_property == "probability") { - return tile_data_float_editor; - } else if (p_property == "y_sort_origin") { - return tile_data_y_sort_editor; - } else if (p_property == "texture_offset") { - return tile_data_texture_offset_editor; - } else if (components.size() >= 1 && components[0].begins_with("occlusion_layer_")) { - return tile_data_occlusion_shape_editor; - } else if (components.size() >= 1 && components[0].begins_with("physics_layer_")) { - return tile_data_collision_shape_editor; - } else if (p_property == "mode" || p_property == "terrain" || (components.size() >= 1 && components[0] == "terrains_peering_bit")) { - return tile_data_terrains_editor; - } else if (components.size() >= 1 && components[0].begins_with("navigation_layer_")) { - return tile_data_navigation_polygon_editor; - } - - return nullptr; -} - void TileSetEditor::edit(Ref p_tile_set) { if (p_tile_set == tile_set) { return; @@ -575,14 +509,4 @@ TileSetEditor::~TileSetEditor() { if (tile_set.is_valid()) { tile_set->disconnect("changed", callable_mp(this, &TileSetEditor::_tile_set_changed)); } - - // Delete tile data editors. - memdelete(tile_data_texture_offset_editor); - memdelete(tile_data_y_sort_editor); - memdelete(tile_data_integer_editor); - memdelete(tile_data_float_editor); - memdelete(tile_data_occlusion_shape_editor); - memdelete(tile_data_collision_shape_editor); - memdelete(tile_data_terrains_editor); - memdelete(tile_data_navigation_polygon_editor); } diff --git a/editor/plugins/tiles/tile_set_editor.h b/editor/plugins/tiles/tile_set_editor.h index f584c043cc..9e50aca62f 100644 --- a/editor/plugins/tiles/tile_set_editor.h +++ b/editor/plugins/tiles/tile_set_editor.h @@ -33,7 +33,6 @@ #include "scene/gui/box_container.h" #include "scene/resources/tile_set.h" -#include "tile_data_editors.h" #include "tile_set_atlas_source_editor.h" #include "tile_set_scenes_collection_source_editor.h" @@ -54,16 +53,6 @@ private: void _update_atlas_sources_list(int force_selected_id = -1); - // List of tile data editors. - TileDataTextureOffsetEditor *tile_data_texture_offset_editor = memnew(TileDataTextureOffsetEditor); - TileDataYSortEditor *tile_data_y_sort_editor = memnew(TileDataYSortEditor); - TileDataIntegerEditor *tile_data_integer_editor = memnew(TileDataIntegerEditor); - TileDataFloatEditor *tile_data_float_editor = memnew(TileDataFloatEditor); - TileDataOcclusionShapeEditor *tile_data_occlusion_shape_editor = memnew(TileDataOcclusionShapeEditor); - TileDataCollisionShapeEditor *tile_data_collision_shape_editor = memnew(TileDataCollisionShapeEditor); - TileDataTerrainsEditor *tile_data_terrains_editor = memnew(TileDataTerrainsEditor); - TileDataNavigationPolygonEditor *tile_data_navigation_polygon_editor = memnew(TileDataNavigationPolygonEditor); - // -- Sources management -- Button *sources_delete_button; MenuButton *sources_add_button; @@ -84,7 +73,6 @@ protected: public: _FORCE_INLINE_ static TileSetEditor *get_singleton() { return singleton; } - TileDataEditor *get_tile_data_editor(String property); void edit(Ref p_tile_set); void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from); bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const; diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp index ef4a53cb0d..4f854ff229 100644 --- a/scene/resources/tile_set.cpp +++ b/scene/resources/tile_set.cpp @@ -30,7 +30,10 @@ #include "tile_set.h" +#include "core/core_string_names.h" #include "core/math/geometry_2d.h" +#include "core/templates/local_vector.h" + #include "scene/2d/navigation_region_2d.h" #include "scene/gui/control.h" #include "scene/resources/convex_polygon_shape_2d.h" @@ -38,6 +41,25 @@ /////////////////////////////// TileSet ////////////////////////////////////// +const char *TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[] = { + "right_side", + "right_corner", + "bottom_right_side", + "bottom_right_corner", + "bottom_side", + "bottom_corner", + "bottom_left_side", + "bottom_left_corner", + "left_side", + "left_corner", + "top_left_side", + "top_left_corner", + "top_side", + "top_corner", + "top_right_side", + "top_right_corner" +}; + // --- Plugins --- Vector TileSet::get_tile_set_atlas_plugins() const { return tile_set_plugins_vector; @@ -51,6 +73,8 @@ void TileSet::set_tile_shape(TileSet::TileShape p_shape) { E_source->get()->notify_tile_data_properties_should_change(); } + terrain_bits_meshes_dirty = true; + tile_meshes_dirty = true; emit_changed(); } TileSet::TileShape TileSet::get_tile_shape() const { @@ -72,6 +96,8 @@ void TileSet::set_tile_offset_axis(TileSet::TileOffsetAxis p_alignment) { E_source->get()->notify_tile_data_properties_should_change(); } + terrain_bits_meshes_dirty = true; + tile_meshes_dirty = true; emit_changed(); } TileSet::TileOffsetAxis TileSet::get_tile_offset_axis() const { @@ -81,20 +107,14 @@ TileSet::TileOffsetAxis TileSet::get_tile_offset_axis() const { void TileSet::set_tile_size(Size2i p_size) { ERR_FAIL_COND(p_size.x < 1 || p_size.y < 1); tile_size = p_size; + terrain_bits_meshes_dirty = true; + tile_meshes_dirty = true; emit_changed(); } Size2i TileSet::get_tile_size() const { return tile_size; } -void TileSet::set_tile_skew(Vector2 p_skew) { - emit_changed(); - tile_skew = p_skew; -} -Vector2 TileSet::get_tile_skew() const { - return tile_skew; -} - int TileSet::get_next_source_id() const { return next_source_id; } @@ -117,7 +137,7 @@ int TileSet::add_source(Ref p_tile_set_source, int p_atlas_source p_tile_set_source->set_tile_set(this); _compute_next_source_id(); - sources[new_source_id]->connect("changed", callable_mp(this, &TileSet::_source_changed)); + sources[new_source_id]->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &TileSet::_source_changed)); emit_changed(); @@ -127,7 +147,7 @@ int TileSet::add_source(Ref p_tile_set_source, int p_atlas_source void TileSet::remove_source(int p_source_id) { ERR_FAIL_COND_MSG(!sources.has(p_source_id), vformat("Cannot remove TileSet atlas source. No tileset atlas source with id %d.", p_source_id)); - sources[p_source_id]->disconnect("changed", callable_mp(this, &TileSet::_source_changed)); + sources[p_source_id]->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &TileSet::_source_changed)); sources[p_source_id]->set_tile_set(nullptr); sources.erase(p_source_id); @@ -240,80 +260,6 @@ bool TileSet::get_occlusion_layer_sdf_collision(int p_layer_index) const { return occlusion_layers[p_layer_index].sdf_collision; } -void TileSet::draw_tile_shape(CanvasItem *p_canvas_item, Rect2 p_region, Color p_color, bool p_filled, Ref p_texture) { - // TODO: optimize this with 2D meshes when they work again. - if (get_tile_shape() == TileSet::TILE_SHAPE_SQUARE) { - if (p_filled && p_texture.is_valid()) { - p_canvas_item->draw_texture_rect(p_texture, p_region, false, p_color); - } else { - p_canvas_item->draw_rect(p_region, p_color, p_filled); - } - } else { - float overlap = 0.0; - switch (get_tile_shape()) { - case TileSet::TILE_SHAPE_ISOMETRIC: - overlap = 0.5; - break; - case TileSet::TILE_SHAPE_HEXAGON: - overlap = 0.25; - break; - case TileSet::TILE_SHAPE_HALF_OFFSET_SQUARE: - overlap = 0.0; - break; - default: - break; - } - - Vector uvs; - uvs.append(Vector2(0.5, 0.0)); - uvs.append(Vector2(0.0, overlap)); - uvs.append(Vector2(0.0, 1.0 - overlap)); - uvs.append(Vector2(0.5, 1.0)); - uvs.append(Vector2(1.0, 1.0 - overlap)); - uvs.append(Vector2(1.0, overlap)); - uvs.append(Vector2(0.5, 0.0)); - if (get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_VERTICAL) { - for (int i = 0; i < uvs.size(); i++) { - uvs.write[i] = Vector2(uvs[i].y, uvs[i].x); - } - } - - Vector points; - for (int i = 0; i < uvs.size(); i++) { - points.append(p_region.position + uvs[i] * p_region.size); - } - - if (p_filled) { - // This does hurt performances a lot. We should use a mesh if possible instead. - p_canvas_item->draw_colored_polygon(points, p_color, uvs, p_texture); - - // Should improve performances, but does not work as draw_primitive does not work with textures :/ : - /*for (int i = 0; i < 6; i += 3) { - Vector quad; - quad.append(points[i]); - quad.append(points[(i + 1) % points.size()]); - quad.append(points[(i + 2) % points.size()]); - quad.append(points[(i + 3) % points.size()]); - - Vector uv_quad; - uv_quad.append(uvs[i]); - uv_quad.append(uvs[(i + 1) % uvs.size()]); - uv_quad.append(uvs[(i + 2) % uvs.size()]); - uv_quad.append(uvs[(i + 3) % uvs.size()]); - - p_control->draw_primitive(quad, Vector(), uv_quad, p_texture); - }*/ - - } else { - // This does hurt performances a lot. We should use a mesh if possible instead. - // tile_shape_grid->draw_polyline(points, p_color); - for (int i = 0; i < points.size() - 1; i++) { - p_canvas_item->draw_line(points[i], points[i + 1], p_color); - } - } - } -} - // Physics void TileSet::set_physics_layers_count(int p_physics_layers_count) { ERR_FAIL_COND(p_physics_layers_count < 0); @@ -459,14 +405,9 @@ Color TileSet::get_terrain_color(int p_terrain_set, int p_terrain_index) const { return terrain_sets[p_terrain_set].terrains[p_terrain_index].color; } -bool TileSet::is_valid_peering_bit_terrain(int p_terrain_set, TileSet::CellNeighbor p_peering_bit) const { - if (p_terrain_set < 0 || p_terrain_set >= get_terrain_sets_count()) { - return false; - } - - TileSet::TerrainMode terrain_mode = get_terrain_set_mode(p_terrain_set); +bool TileSet::is_valid_peering_bit_for_mode(TileSet::TerrainMode p_terrain_mode, TileSet::CellNeighbor p_peering_bit) const { if (tile_shape == TileSet::TILE_SHAPE_SQUARE) { - if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || terrain_mode == TileSet::TERRAIN_MODE_MATCH_SIDES) { + if (p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_SIDES) { if (p_peering_bit == TileSet::CELL_NEIGHBOR_RIGHT_SIDE || p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_SIDE || p_peering_bit == TileSet::CELL_NEIGHBOR_LEFT_SIDE || @@ -474,7 +415,7 @@ bool TileSet::is_valid_peering_bit_terrain(int p_terrain_set, TileSet::CellNeigh return true; } } - if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { + if (p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { if (p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER || p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER || p_peering_bit == TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER || @@ -483,7 +424,7 @@ bool TileSet::is_valid_peering_bit_terrain(int p_terrain_set, TileSet::CellNeigh } } } else if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC) { - if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || terrain_mode == TileSet::TERRAIN_MODE_MATCH_SIDES) { + if (p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_SIDES) { if (p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE || p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE || p_peering_bit == TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE || @@ -491,7 +432,7 @@ bool TileSet::is_valid_peering_bit_terrain(int p_terrain_set, TileSet::CellNeigh return true; } } - if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { + if (p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { if (p_peering_bit == TileSet::CELL_NEIGHBOR_RIGHT_CORNER || p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_CORNER || p_peering_bit == TileSet::CELL_NEIGHBOR_LEFT_CORNER || @@ -501,7 +442,7 @@ bool TileSet::is_valid_peering_bit_terrain(int p_terrain_set, TileSet::CellNeigh } } else { if (get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { - if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || terrain_mode == TileSet::TERRAIN_MODE_MATCH_SIDES) { + if (p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_SIDES) { if (p_peering_bit == TileSet::CELL_NEIGHBOR_RIGHT_SIDE || p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE || p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE || @@ -511,7 +452,7 @@ bool TileSet::is_valid_peering_bit_terrain(int p_terrain_set, TileSet::CellNeigh return true; } } - if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { + if (p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { if (p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER || p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_CORNER || p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER || @@ -522,7 +463,7 @@ bool TileSet::is_valid_peering_bit_terrain(int p_terrain_set, TileSet::CellNeigh } } } else { - if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || terrain_mode == TileSet::TERRAIN_MODE_MATCH_SIDES) { + if (p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_SIDES) { if (p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE || p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_SIDE || p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE || @@ -532,7 +473,7 @@ bool TileSet::is_valid_peering_bit_terrain(int p_terrain_set, TileSet::CellNeigh return true; } } - if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { + if (p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES || p_terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { if (p_peering_bit == TileSet::CELL_NEIGHBOR_RIGHT_CORNER || p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER || p_peering_bit == TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER || @@ -547,6 +488,15 @@ bool TileSet::is_valid_peering_bit_terrain(int p_terrain_set, TileSet::CellNeigh return false; } +bool TileSet::is_valid_peering_bit_terrain(int p_terrain_set, TileSet::CellNeighbor p_peering_bit) const { + if (p_terrain_set < 0 || p_terrain_set >= get_terrain_sets_count()) { + return false; + } + + TileSet::TerrainMode terrain_mode = get_terrain_set_mode(p_terrain_set); + return is_valid_peering_bit_for_mode(terrain_mode, p_peering_bit); +} + // Navigation void TileSet::set_navigation_layers_count(int p_navigation_layers_count) { ERR_FAIL_COND(p_navigation_layers_count < 0); @@ -657,2119 +607,2446 @@ Variant::Type TileSet::get_custom_data_type(int p_layer_id) const { return custom_data_layers[p_layer_id].type; } -void TileSet::_source_changed() { - emit_changed(); - notify_property_list_changed(); -} +Vector TileSet::get_tile_shape_polygon() { + Vector points; + if (tile_shape == TileSet::TILE_SHAPE_SQUARE) { + points.append(Vector2(0.0, 0.0)); + points.append(Vector2(1.0, 0.0)); + points.append(Vector2(1.0, 1.0)); + points.append(Vector2(0.0, 1.0)); + } else { + float overlap = 0.0; + switch (tile_shape) { + case TileSet::TILE_SHAPE_ISOMETRIC: + overlap = 0.5; + break; + case TileSet::TILE_SHAPE_HEXAGON: + overlap = 0.25; + break; + case TileSet::TILE_SHAPE_HALF_OFFSET_SQUARE: + overlap = 0.0; + break; + default: + break; + } -void TileSet::reset_state() { - occlusion_layers.clear(); - physics_layers.clear(); - custom_data_layers.clear(); + points.append(Vector2(0.5, 0.0)); + points.append(Vector2(0.0, overlap)); + points.append(Vector2(0.0, 1.0 - overlap)); + points.append(Vector2(0.5, 1.0)); + points.append(Vector2(1.0, 1.0 - overlap)); + points.append(Vector2(1.0, overlap)); + points.append(Vector2(0.5, 0.0)); + if (get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_VERTICAL) { + for (int i = 0; i < points.size(); i++) { + points.write[i] = Vector2(points[i].y, points[i].x); + } + } + } + for (int i = 0; i < points.size(); i++) { + points.write[i] = points[i] * tile_size - tile_size / 2; + } + return points; } -const Vector2i TileSetSource::INVALID_ATLAS_COORDS = Vector2i(-1, -1); -const int TileSetSource::INVALID_TILE_ALTERNATIVE = -1; +void TileSet::draw_tile_shape(CanvasItem *p_canvas_item, Rect2 p_region, Color p_color, bool p_filled, Ref p_texture) { + if (tile_meshes_dirty) { + Vector uvs = get_tile_shape_polygon(); + for (int i = 0; i < uvs.size(); i++) { + uvs.write[i] = (uvs[i] + tile_size / 2) / tile_size; + } -#ifndef DISABLE_DEPRECATED -void TileSet::compatibility_conversion() { - for (Map::Element *E = compatibility_data.front(); E; E = E->next()) { - CompatibilityTileData *ctd = E->value(); + Vector colors; + colors.resize(uvs.size()); + colors.fill(Color(1.0, 1.0, 1.0, 1.0)); + + // Filled mesh. + tile_filled_mesh->clear_surfaces(); + Array a; + a.resize(Mesh::ARRAY_MAX); + a[Mesh::ARRAY_VERTEX] = uvs; + a[Mesh::ARRAY_TEX_UV] = uvs; + a[Mesh::ARRAY_COLOR] = colors; + a[Mesh::ARRAY_INDEX] = Geometry2D::triangulate_polygon(uvs); + tile_filled_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, a, Array(), Dictionary(), Mesh::ARRAY_FLAG_USE_2D_VERTICES); + + // Lines mesh. + tile_lines_mesh->clear_surfaces(); + a.clear(); + a.resize(Mesh::ARRAY_MAX); + // Add the first point again when drawing lines. + uvs.push_back(uvs[0]); + colors.push_back(colors[0]); + a[Mesh::ARRAY_VERTEX] = uvs; + a[Mesh::ARRAY_COLOR] = colors; + tile_lines_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_LINE_STRIP, a, Array(), Dictionary(), Mesh::ARRAY_FLAG_USE_2D_VERTICES); + + tile_meshes_dirty = false; + } + + Transform2D xform; + xform.scale(p_region.size); + xform.set_origin(p_region.get_position()); + if (p_filled) { + p_canvas_item->draw_mesh(tile_filled_mesh, p_texture, xform, p_color); + } else { + p_canvas_item->draw_mesh(tile_lines_mesh, Ref(), xform, p_color); + } +} - // Add the texture - TileSetAtlasSource *atlas_source = memnew(TileSetAtlasSource); - int source_id = add_source(Ref(atlas_source)); +Vector TileSet::get_terrain_bit_polygon(int p_terrain_set, TileSet::CellNeighbor p_bit) { + ERR_FAIL_COND_V(p_terrain_set < 0 || p_terrain_set >= get_terrain_sets_count(), Vector()); - atlas_source->set_texture(ctd->texture); + TileSet::TerrainMode terrain_mode = get_terrain_set_mode(p_terrain_set); - // Handle each tile as a new source. Not optimal but at least it should stay compatible. - switch (ctd->tile_mode) { - case 0: // SINGLE_TILE - // TODO + if (tile_shape == TileSet::TILE_SHAPE_SQUARE) { + if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES) { + return _get_square_corner_or_side_terrain_bit_polygon(tile_size, p_bit); + } else if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { + return _get_square_corner_terrain_bit_polygon(tile_size, p_bit); + } else { // TileData::TERRAIN_MODE_MATCH_SIDES + return _get_square_side_terrain_bit_polygon(tile_size, p_bit); + } + } else if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC) { + if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES) { + return _get_isometric_corner_or_side_terrain_bit_polygon(tile_size, p_bit); + } else if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { + return _get_isometric_corner_terrain_bit_polygon(tile_size, p_bit); + } else { // TileData::TERRAIN_MODE_MATCH_SIDES + return _get_isometric_side_terrain_bit_polygon(tile_size, p_bit); + } + } else { + float overlap = 0.0; + switch (tile_shape) { + case TileSet::TILE_SHAPE_HEXAGON: + overlap = 0.25; break; - case 1: // AUTO_TILE - // TODO + case TileSet::TILE_SHAPE_HALF_OFFSET_SQUARE: + overlap = 0.0; break; - case 2: // ATLAS_TILE - atlas_source->set_margins(ctd->region.get_position()); - atlas_source->set_separation(Vector2i(ctd->autotile_spacing, ctd->autotile_spacing)); - atlas_source->set_texture_region_size(ctd->autotile_tile_size); + default: + break; + } + if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES) { + return _get_half_offset_corner_or_side_terrain_bit_polygon(tile_size, p_bit, overlap, tile_offset_axis); + } else if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { + return _get_half_offset_corner_terrain_bit_polygon(tile_size, p_bit, overlap, tile_offset_axis); + } else { // TileData::TERRAIN_MODE_MATCH_SIDES + return _get_half_offset_side_terrain_bit_polygon(tile_size, p_bit, overlap, tile_offset_axis); + } + } +} - Size2i atlas_size = ctd->region.get_size() / (ctd->autotile_tile_size + atlas_source->get_separation()); - for (int i = 0; i < atlas_size.x; i++) { - for (int j = 0; j < atlas_size.y; j++) { - Vector2i coords = Vector2i(i, j); +#define TERRAIN_ALPHA 0.6 - for (int flags = 0; flags < 8; flags++) { - bool flip_h = flags & 1; - bool flip_v = flags & 2; - bool transpose = flags & 4; +void TileSet::draw_terrains(CanvasItem *p_canvas_item, Transform2D p_transform, const TileData *p_tile_data) { + ERR_FAIL_COND(!p_tile_data); - int alternative_tile = 0; - if (!atlas_source->has_tile(coords)) { - atlas_source->create_tile(coords); - } else { - alternative_tile = atlas_source->create_alternative_tile(coords); - } - TileData *tile_data = Object::cast_to(atlas_source->get_tile_data(coords, alternative_tile)); + if (terrain_bits_meshes_dirty) { + // Recompute the meshes. + terrain_bits_meshes.clear(); + + for (int terrain_mode_index = 0; terrain_mode_index < 3; terrain_mode_index++) { + TerrainMode terrain_mode = TerrainMode(terrain_mode_index); + for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { + CellNeighbor bit = CellNeighbor(i); + + if (is_valid_peering_bit_for_mode(terrain_mode, bit)) { + Vector polygon; + if (tile_shape == TileSet::TILE_SHAPE_SQUARE) { + if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES) { + polygon = _get_square_corner_or_side_terrain_bit_polygon(tile_size, bit); + } else if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { + polygon = _get_square_corner_terrain_bit_polygon(tile_size, bit); + } else { // TileData::TERRAIN_MODE_MATCH_SIDES + polygon = _get_square_side_terrain_bit_polygon(tile_size, bit); + } + } else if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC) { + if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES) { + polygon = _get_isometric_corner_or_side_terrain_bit_polygon(tile_size, bit); + } else if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { + polygon = _get_isometric_corner_terrain_bit_polygon(tile_size, bit); + } else { // TileData::TERRAIN_MODE_MATCH_SIDES + polygon = _get_isometric_side_terrain_bit_polygon(tile_size, bit); + } + } else { + float overlap = 0.0; + switch (tile_shape) { + case TileSet::TILE_SHAPE_HEXAGON: + overlap = 0.25; + break; + case TileSet::TILE_SHAPE_HALF_OFFSET_SQUARE: + overlap = 0.0; + break; + default: + break; + } + if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES) { + polygon = _get_half_offset_corner_or_side_terrain_bit_polygon(tile_size, bit, overlap, tile_offset_axis); + } else if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { + polygon = _get_half_offset_corner_terrain_bit_polygon(tile_size, bit, overlap, tile_offset_axis); + } else { // TileData::TERRAIN_MODE_MATCH_SIDES + polygon = _get_half_offset_side_terrain_bit_polygon(tile_size, bit, overlap, tile_offset_axis); + } + } - tile_data->set_flip_h(flip_h); - tile_data->set_flip_v(flip_v); - tile_data->set_transpose(transpose); - tile_data->tile_set_material(ctd->material); - tile_data->set_modulate(ctd->modulate); - tile_data->set_z_index(ctd->z_index); - if (ctd->autotile_occluder_map.has(coords)) { - if (get_occlusion_layers_count() < 1) { - set_occlusion_layers_count(1); - } - tile_data->set_occluder(0, ctd->autotile_occluder_map[coords]); - } - if (ctd->autotile_navpoly_map.has(coords)) { - if (get_navigation_layers_count() < 1) { - set_navigation_layers_count(1); - } - tile_data->set_navigation_polygon(0, ctd->autotile_navpoly_map[coords]); - } - if (ctd->autotile_priority_map.has(coords)) { - tile_data->set_probability(ctd->autotile_priority_map[coords]); - } - if (ctd->autotile_z_index_map.has(coords)) { - tile_data->set_z_index(ctd->autotile_z_index_map[coords]); - } + Ref mesh; + mesh.instantiate(); + Vector uvs; + uvs.resize(polygon.size()); + Vector colors; + colors.resize(polygon.size()); + colors.fill(Color(1.0, 1.0, 1.0, 1.0)); + Array a; + a.resize(Mesh::ARRAY_MAX); + a[Mesh::ARRAY_VERTEX] = polygon; + a[Mesh::ARRAY_TEX_UV] = uvs; + a[Mesh::ARRAY_COLOR] = colors; + a[Mesh::ARRAY_INDEX] = Geometry2D::triangulate_polygon(polygon); + mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, a, Array(), Dictionary(), Mesh::ARRAY_FLAG_USE_2D_VERTICES); + terrain_bits_meshes[terrain_mode][bit] = mesh; + } + } + } + terrain_bits_meshes_dirty = false; + } - // Add the shapes. - if (ctd->shapes.size() > 0) { - if (get_physics_layers_count() < 1) { - set_physics_layers_count(1); - } - } - for (int k = 0; k < ctd->shapes.size(); k++) { - CompatibilityShapeData csd = ctd->shapes[k]; - if (csd.autotile_coords == coords) { - tile_data->set_collision_shapes_count(0, tile_data->get_collision_shapes_count(0) + 1); - int index = tile_data->get_collision_shapes_count(0) - 1; - tile_data->set_collision_shape_one_way(0, index, csd.one_way); - tile_data->set_collision_shape_one_way_margin(0, index, csd.one_way_margin); - tile_data->set_collision_shape_shape(0, index, csd.shape); - // Ignores transform for now. + int terrain_set = p_tile_data->get_terrain_set(); + if (terrain_set < 0) { + return; + } + TileSet::TerrainMode terrain_mode = get_terrain_set_mode(terrain_set); + + RenderingServer::get_singleton()->canvas_item_add_set_transform(p_canvas_item->get_canvas_item(), p_transform); + for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { + CellNeighbor bit = CellNeighbor(i); + if (is_valid_peering_bit_terrain(terrain_set, bit)) { + int terrain_id = p_tile_data->get_peering_bit_terrain(bit); + if (terrain_id >= 0) { + Color color = get_terrain_color(terrain_set, terrain_id); + color.a = TERRAIN_ALPHA; + p_canvas_item->draw_mesh(terrain_bits_meshes[terrain_mode][bit], Ref(), Transform2D(), color); + } + } + } + RenderingServer::get_singleton()->canvas_item_add_set_transform(p_canvas_item->get_canvas_item(), Transform2D()); +} + +Vector>> TileSet::generate_terrains_icons(Size2i p_size) { + // Counts the number of matching terrain tiles and find the best matching icon. + struct Count { + int count = 0; + float probability = 0.0; + Ref texture; + Rect2i region; + }; + Vector>> output; + LocalVector> counts; + output.resize(get_terrain_sets_count()); + counts.resize(get_terrain_sets_count()); + for (int terrain_set = 0; terrain_set < get_terrain_sets_count(); terrain_set++) { + output.write[terrain_set].resize(get_terrains_count(terrain_set)); + counts[terrain_set].resize(get_terrains_count(terrain_set)); + } + + for (int source_index = 0; source_index < get_source_count(); source_index++) { + int source_id = get_source_id(source_index); + Ref source = get_source(source_id); + + Ref atlas_source = source; + if (atlas_source.is_valid()) { + for (int tile_index = 0; tile_index < source->get_tiles_count(); tile_index++) { + Vector2i tile_id = source->get_tile_id(tile_index); + for (int alternative_index = 0; alternative_index < source->get_alternative_tiles_count(tile_id); alternative_index++) { + int alternative_id = source->get_alternative_tile_id(tile_id, alternative_index); + + TileData *tile_data = Object::cast_to(atlas_source->get_tile_data(tile_id, alternative_id)); + int terrain_set = tile_data->get_terrain_set(); + if (terrain_set >= 0) { + ERR_FAIL_INDEX_V(terrain_set, get_terrain_sets_count(), Vector>>()); + + LocalVector bit_counts; + bit_counts.resize(get_terrains_count(terrain_set)); + for (int terrain = 0; terrain < get_terrains_count(terrain_set); terrain++) { + bit_counts[terrain] = 0; + } + for (int terrain_bit = 0; terrain_bit < TileSet::CELL_NEIGHBOR_MAX; terrain_bit++) { + TileSet::CellNeighbor cell_neighbor = TileSet::CellNeighbor(terrain_bit); + if (is_valid_peering_bit_terrain(terrain_set, cell_neighbor)) { + int terrain = tile_data->get_peering_bit_terrain(cell_neighbor); + if (terrain >= 0) { + bit_counts[terrain] += 1; } } + } - // -- TODO: handle -- - // Those are offset for the whole atlas, they are likely useless for the atlases, but might make sense for single tiles. - // texture offset - // occluder_offset - // navigation_offset - - // For terrains, ignored for now? - // bitmask_mode - // bitmask_flags + for (int terrain = 0; terrain < get_terrains_count(terrain_set); terrain++) { + if ((bit_counts[terrain] > counts[terrain_set][terrain].count) || (bit_counts[terrain] == counts[terrain_set][terrain].count && tile_data->get_probability() > counts[terrain_set][terrain].probability)) { + counts[terrain_set][terrain].count = bit_counts[terrain]; + counts[terrain_set][terrain].probability = tile_data->get_probability(); + counts[terrain_set][terrain].texture = atlas_source->get_texture(); + counts[terrain_set][terrain].region = atlas_source->get_tile_texture_region(tile_id); + } } } } - break; - } - - // Offset all shapes - for (int k = 0; k < ctd->shapes.size(); k++) { - Ref convex = ctd->shapes[k].shape; - if (convex.is_valid()) { - Vector points = convex->get_points(); - for (int i_point = 0; i_point < points.size(); i_point++) { - points.write[i_point] = points[i_point] - get_tile_size() / 2; - } - convex->set_points(points); } } - - // Add the mapping to the map - compatibility_source_mapping.insert(E->key(), source_id); } - // Reset compatibility data - for (Map::Element *E = compatibility_data.front(); E; E = E->next()) { - memdelete(E->get()); + // Generate the icons. + for (int terrain_set = 0; terrain_set < get_terrain_sets_count(); terrain_set++) { + for (int terrain = 0; terrain < get_terrains_count(terrain_set); terrain++) { + Ref image; + image.instantiate(); + if (counts[terrain_set][terrain].count > 0) { + // Get the best tile. + Ref texture = counts[terrain_set][terrain].texture; + Rect2 region = counts[terrain_set][terrain].region; + image->create(region.size.x, region.size.y, false, Image::FORMAT_RGBA8); + image->blit_rect(texture->get_image(), region, Point2()); + image->resize(p_size.x, p_size.y, Image::INTERPOLATE_NEAREST); + } else { + image->create(1, 1, false, Image::FORMAT_RGBA8); + image->set_pixel(0, 0, get_terrain_color(terrain_set, terrain)); + } + Ref icon; + icon.instantiate(); + icon->create_from_image(image); + icon->set_size_override(p_size); + + output.write[terrain_set].write[terrain] = icon; + } } - compatibility_data = Map(); + return output; } -#endif // DISABLE_DEPRECATED -bool TileSet::_set(const StringName &p_name, const Variant &p_value) { - Vector components = String(p_name).split("/", true, 2); - -#ifndef DISABLE_DEPRECATED - // TODO: THIS IS HOW WE CHECK IF WE HAVE A DEPRECATED RESOURCE - // This should be moved to a dedicated conversion system - if (components.size() >= 1 && components[0].is_valid_int()) { - int id = components[0].to_int(); +void TileSet::_source_changed() { + emit_changed(); +} - // Get or create the compatibility object - CompatibilityTileData *ctd; - Map::Element *E = compatibility_data.find(id); - if (!E) { - ctd = memnew(CompatibilityTileData); - compatibility_data.insert(id, ctd); - } else { - ctd = E->get(); - } - - if (components.size() < 2) { - return false; - } - - String what = components[1]; - - if (what == "name") { - ctd->name = p_value; - } else if (what == "texture") { - ctd->texture = p_value; - } else if (what == "tex_offset") { - ctd->tex_offset = p_value; - } else if (what == "material") { - ctd->material = p_value; - } else if (what == "modulate") { - ctd->modulate = p_value; - } else if (what == "region") { - ctd->region = p_value; - } else if (what == "tile_mode") { - ctd->tile_mode = p_value; - } else if (what.left(9) == "autotile") { - what = what.substr(9); - if (what == "bitmask_mode") { - ctd->autotile_bitmask_mode = p_value; - } else if (what == "icon_coordinate") { - ctd->autotile_icon_coordinate = p_value; - } else if (what == "tile_size") { - ctd->autotile_tile_size = p_value; - } else if (what == "spacing") { - ctd->autotile_spacing = p_value; - } else if (what == "bitmask_flags") { - if (p_value.is_array()) { - Array p = p_value; - Vector2i last_coord; - while (p.size() > 0) { - if (p[0].get_type() == Variant::VECTOR2) { - last_coord = p[0]; - } else if (p[0].get_type() == Variant::INT) { - ctd->autotile_bitmask_flags.insert(last_coord, p[0]); - } - p.pop_front(); - } - } - } else if (what == "occluder_map") { - Array p = p_value; - Vector2 last_coord; - while (p.size() > 0) { - if (p[0].get_type() == Variant::VECTOR2) { - last_coord = p[0]; - } else if (p[0].get_type() == Variant::OBJECT) { - ctd->autotile_occluder_map.insert(last_coord, p[0]); - } - p.pop_front(); - } - } else if (what == "navpoly_map") { - Array p = p_value; - Vector2 last_coord; - while (p.size() > 0) { - if (p[0].get_type() == Variant::VECTOR2) { - last_coord = p[0]; - } else if (p[0].get_type() == Variant::OBJECT) { - ctd->autotile_navpoly_map.insert(last_coord, p[0]); - } - p.pop_front(); - } - } else if (what == "priority_map") { - Array p = p_value; - Vector3 val; - Vector2 v; - int priority; - while (p.size() > 0) { - val = p[0]; - if (val.z > 1) { - v.x = val.x; - v.y = val.y; - priority = (int)val.z; - ctd->autotile_priority_map.insert(v, priority); - } - p.pop_front(); - } - } else if (what == "z_index_map") { - Array p = p_value; - Vector3 val; - Vector2 v; - int z_index; - while (p.size() > 0) { - val = p[0]; - if (val.z != 0) { - v.x = val.x; - v.y = val.y; - z_index = (int)val.z; - ctd->autotile_z_index_map.insert(v, z_index); - } - p.pop_front(); - } - } - - } else if (what == "shapes") { - Array p = p_value; - for (int i = 0; i < p.size(); i++) { - CompatibilityShapeData csd; - Dictionary d = p[i]; - for (int j = 0; j < d.size(); j++) { - String key = d.get_key_at_index(j); - if (key == "autotile_coord") { - csd.autotile_coords = d[key]; - } else if (key == "one_way") { - csd.one_way = d[key]; - } else if (key == "one_way_margin") { - csd.one_way_margin = d[key]; - } else if (key == "shape") { - csd.shape = d[key]; - } else if (key == "shape_transform") { - csd.transform = d[key]; - } - } - ctd->shapes.push_back(csd); - } - - /* - // IGNORED FOR NOW, they seem duplicated data compared to the shapes array - } else if (what == "shape") { - // TODO - } else if (what == "shape_offset") { - // TODO - } else if (what == "shape_transform") { - // TODO - } else if (what == "shape_one_way") { - // TODO - } else if (what == "shape_one_way_margin") { - // TODO - } - // IGNORED FOR NOW, maybe useless ? - else if (what == "occluder_offset") { - // Not - } else if (what == "navigation_offset") { - // TODO - } - */ - - } else if (what == "z_index") { - ctd->z_index = p_value; - - // TODO: remove the conversion from here, it's not where it should be done - compatibility_conversion(); - } else { - return false; - } - } else { -#endif // DISABLE_DEPRECATED - - // This is now a new property. - if (components.size() == 2 && components[0].begins_with("occlusion_layer_") && components[0].trim_prefix("occlusion_layer_").is_valid_int()) { - // Occlusion layers. - int index = components[0].trim_prefix("occlusion_layer_").to_int(); - ERR_FAIL_COND_V(index < 0, false); - if (components[1] == "light_mask") { - ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false); - if (index >= occlusion_layers.size()) { - set_occlusion_layers_count(index + 1); - } - set_occlusion_layer_light_mask(index, p_value); - return true; - } else if (components[1] == "sdf_collision") { - ERR_FAIL_COND_V(p_value.get_type() != Variant::BOOL, false); - if (index >= occlusion_layers.size()) { - set_occlusion_layers_count(index + 1); - } - set_occlusion_layer_sdf_collision(index, p_value); - return true; - } - } else if (components.size() == 2 && components[0].begins_with("physics_layer_") && components[0].trim_prefix("physics_layer_").is_valid_int()) { - // Physics layers. - int index = components[0].trim_prefix("physics_layer_").to_int(); - ERR_FAIL_COND_V(index < 0, false); - if (components[1] == "collision_layer") { - ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false); - if (index >= physics_layers.size()) { - set_physics_layers_count(index + 1); - } - set_physics_layer_collision_layer(index, p_value); - return true; - } else if (components[1] == "collision_mask") { - ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false); - if (index >= physics_layers.size()) { - set_physics_layers_count(index + 1); - } - set_physics_layer_collision_mask(index, p_value); - return true; - } else if (components[1] == "physics_material") { - Ref physics_material = p_value; - ERR_FAIL_COND_V(!physics_material.is_valid(), false); - if (index >= physics_layers.size()) { - set_physics_layers_count(index + 1); - } - set_physics_layer_physics_material(index, physics_material); - return true; - } - } else if (components.size() >= 2 && components[0].begins_with("terrain_set_") && components[0].trim_prefix("terrain_set_").is_valid_int()) { - // Terrains. - int terrain_set_index = components[0].trim_prefix("terrain_set_").to_int(); - ERR_FAIL_COND_V(terrain_set_index < 0, false); - if (components[1] == "mode") { - ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false); - if (terrain_set_index >= terrain_sets.size()) { - set_terrain_sets_count(terrain_set_index + 1); - } - set_terrain_set_mode(terrain_set_index, TerrainMode(int(p_value))); - } else if (components[1] == "terrains_count") { - ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false); - if (terrain_set_index >= terrain_sets.size()) { - set_terrain_sets_count(terrain_set_index + 1); - } - set_terrains_count(terrain_set_index, p_value); - return true; - } else if (components.size() >= 3 && components[1].begins_with("terrain_") && components[1].trim_prefix("terrain_").is_valid_int()) { - int terrain_index = components[1].trim_prefix("terrain_").to_int(); - ERR_FAIL_COND_V(terrain_index < 0, false); - if (components[2] == "name") { - ERR_FAIL_COND_V(p_value.get_type() != Variant::STRING, false); - if (terrain_set_index >= terrain_sets.size()) { - set_terrain_sets_count(terrain_set_index + 1); - } - if (terrain_index >= terrain_sets[terrain_set_index].terrains.size()) { - set_terrains_count(terrain_set_index, terrain_index + 1); - } - set_terrain_name(terrain_set_index, terrain_index, p_value); - return true; - } else if (components[2] == "color") { - ERR_FAIL_COND_V(p_value.get_type() != Variant::COLOR, false); - if (terrain_set_index >= terrain_sets.size()) { - set_terrain_sets_count(terrain_set_index + 1); - } - if (terrain_index >= terrain_sets[terrain_set_index].terrains.size()) { - set_terrains_count(terrain_set_index, terrain_index + 1); - } - set_terrain_color(terrain_set_index, terrain_index, p_value); - return true; - } - } - } else if (components.size() == 2 && components[0].begins_with("navigation_layer_") && components[0].trim_prefix("navigation_layer_").is_valid_int()) { - // Navigation layers. - int index = components[0].trim_prefix("navigation_layer_").to_int(); - ERR_FAIL_COND_V(index < 0, false); - if (components[1] == "layers") { - ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false); - if (index >= navigation_layers.size()) { - set_navigation_layers_count(index + 1); - } - set_navigation_layer_layers(index, p_value); - return true; - } - } else if (components.size() == 2 && components[0].begins_with("custom_data_layer_") && components[0].trim_prefix("custom_data_layer_").is_valid_int()) { - // Custom data layers. - int index = components[0].trim_prefix("custom_data_layer_").to_int(); - ERR_FAIL_COND_V(index < 0, false); - if (components[1] == "name") { - ERR_FAIL_COND_V(p_value.get_type() != Variant::STRING, false); - if (index >= custom_data_layers.size()) { - set_custom_data_layers_count(index + 1); - } - set_custom_data_name(index, p_value); - return true; - } else if (components[1] == "type") { - ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false); - if (index >= custom_data_layers.size()) { - set_custom_data_layers_count(index + 1); - } - set_custom_data_type(index, Variant::Type(int(p_value))); - return true; - } - } else if (components.size() == 2 && components[0] == "sources" && components[1].is_valid_int()) { - // Create source only if it does not exist. - int source_id = components[1].to_int(); - - if (!has_source(source_id)) { - add_source(p_value, source_id); - } - return true; - } - -#ifndef DISABLE_DEPRECATED - } -#endif // DISABLE_DEPRECATED - - return false; -} - -bool TileSet::_get(const StringName &p_name, Variant &r_ret) const { - Vector components = String(p_name).split("/", true, 2); - - if (components.size() == 2 && components[0].begins_with("occlusion_layer_") && components[0].trim_prefix("occlusion_layer_").is_valid_int()) { - // Occlusion layers. - int index = components[0].trim_prefix("occlusion_layer_").to_int(); - if (index < 0 || index >= occlusion_layers.size()) { - return false; - } - if (components[1] == "light_mask") { - r_ret = get_occlusion_layer_light_mask(index); - return true; - } else if (components[1] == "sdf_collision") { - r_ret = get_occlusion_layer_sdf_collision(index); - return true; - } - } else if (components.size() == 2 && components[0].begins_with("physics_layer_") && components[0].trim_prefix("physics_layer_").is_valid_int()) { - // Physics layers. - int index = components[0].trim_prefix("physics_layer_").to_int(); - if (index < 0 || index >= physics_layers.size()) { - return false; - } - if (components[1] == "collision_layer") { - r_ret = get_physics_layer_collision_layer(index); - return true; - } else if (components[1] == "collision_mask") { - r_ret = get_physics_layer_collision_mask(index); - return true; - } else if (components[1] == "physics_material") { - r_ret = get_physics_layer_physics_material(index); - return true; - } - } else if (components.size() >= 2 && components[0].begins_with("terrain_set_") && components[0].trim_prefix("terrain_set_").is_valid_int()) { - // Terrains. - int terrain_set_index = components[0].trim_prefix("terrain_set_").to_int(); - if (terrain_set_index < 0 || terrain_set_index >= terrain_sets.size()) { - return false; - } - if (components[1] == "mode") { - r_ret = get_terrain_set_mode(terrain_set_index); - return true; - } else if (components[1] == "terrains_count") { - r_ret = get_terrains_count(terrain_set_index); - return true; - } else if (components.size() >= 3 && components[1].begins_with("terrain_") && components[1].trim_prefix("terrain_").is_valid_int()) { - int terrain_index = components[1].trim_prefix("terrain_").to_int(); - if (terrain_index < 0 || terrain_index >= terrain_sets[terrain_set_index].terrains.size()) { - return false; - } - if (components[2] == "name") { - r_ret = get_terrain_name(terrain_set_index, terrain_index); - return true; - } else if (components[2] == "color") { - r_ret = get_terrain_color(terrain_set_index, terrain_index); - return true; - } - } - } else if (components.size() == 2 && components[0].begins_with("navigation_layer_") && components[0].trim_prefix("navigation_layer_").is_valid_int()) { - // navigation layers. - int index = components[0].trim_prefix("navigation_layer_").to_int(); - if (index < 0 || index >= navigation_layers.size()) { - return false; - } - if (components[1] == "layers") { - r_ret = get_navigation_layer_layers(index); - return true; - } - } else if (components.size() == 2 && components[0].begins_with("custom_data_layer_") && components[0].trim_prefix("custom_data_layer_").is_valid_int()) { - // Custom data layers. - int index = components[0].trim_prefix("custom_data_layer_").to_int(); - if (index < 0 || index >= custom_data_layers.size()) { - return false; - } - if (components[1] == "name") { - r_ret = get_custom_data_name(index); - return true; - } else if (components[1] == "type") { - r_ret = get_custom_data_type(index); - return true; - } - } else if (components.size() == 2 && components[0] == "sources" && components[1].is_valid_int()) { - // Atlases data. - int source_id = components[1].to_int(); - - if (has_source(source_id)) { - r_ret = get_source(source_id); - return true; - } else { - return false; - } +Vector TileSet::_get_square_corner_or_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit) { + Rect2 bit_rect; + bit_rect.size = Vector2(p_size) / 3; + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_RIGHT_SIDE: + bit_rect.position = Vector2(1, -1); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: + bit_rect.position = Vector2(1, 1); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE: + bit_rect.position = Vector2(-1, 1); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER: + bit_rect.position = Vector2(-3, 1); + break; + case TileSet::CELL_NEIGHBOR_LEFT_SIDE: + bit_rect.position = Vector2(-3, -1); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER: + bit_rect.position = Vector2(-3, -3); + break; + case TileSet::CELL_NEIGHBOR_TOP_SIDE: + bit_rect.position = Vector2(-1, -3); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER: + bit_rect.position = Vector2(1, -3); + break; + default: + break; } - - return false; + bit_rect.position *= Vector2(p_size) / 6.0; + Vector polygon; + polygon.push_back(bit_rect.position); + polygon.push_back(Vector2(bit_rect.get_end().x, bit_rect.position.y)); + polygon.push_back(bit_rect.get_end()); + polygon.push_back(Vector2(bit_rect.position.x, bit_rect.get_end().y)); + return polygon; } -void TileSet::_get_property_list(List *p_list) const { - PropertyInfo property_info; - // Rendering. - p_list->push_back(PropertyInfo(Variant::NIL, "Rendering", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP)); - for (int i = 0; i < occlusion_layers.size(); i++) { - p_list->push_back(PropertyInfo(Variant::INT, vformat("occlusion_layer_%d/light_mask", i), PROPERTY_HINT_LAYERS_2D_RENDER)); - - // occlusion_layer_%d/sdf_collision - property_info = PropertyInfo(Variant::BOOL, vformat("occlusion_layer_%d/sdf_collision", i)); - if (occlusion_layers[i].sdf_collision == false) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; - } - p_list->push_back(property_info); - } - - // Physics. - p_list->push_back(PropertyInfo(Variant::NIL, "Physics", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP)); - for (int i = 0; i < physics_layers.size(); i++) { - p_list->push_back(PropertyInfo(Variant::INT, vformat("physics_layer_%d/collision_layer", i), PROPERTY_HINT_LAYERS_2D_PHYSICS)); - - // physics_layer_%d/collision_mask - property_info = PropertyInfo(Variant::INT, vformat("physics_layer_%d/collision_mask", i), PROPERTY_HINT_LAYERS_2D_PHYSICS); - if (physics_layers[i].collision_mask == 1) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; - } - p_list->push_back(property_info); - - // physics_layer_%d/physics_material - property_info = PropertyInfo(Variant::OBJECT, vformat("physics_layer_%d/physics_material", i), PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"); - if (!physics_layers[i].physics_material.is_valid()) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; - } - p_list->push_back(property_info); - } - - // Terrains. - p_list->push_back(PropertyInfo(Variant::NIL, "Terrains", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP)); - for (int terrain_set_index = 0; terrain_set_index < terrain_sets.size(); terrain_set_index++) { - p_list->push_back(PropertyInfo(Variant::INT, vformat("terrain_set_%d/mode", terrain_set_index), PROPERTY_HINT_ENUM, "Match corners and sides,Match corners,Match sides")); - p_list->push_back(PropertyInfo(Variant::INT, vformat("terrain_set_%d/terrains_count", terrain_set_index), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR)); - for (int terrain_index = 0; terrain_index < terrain_sets[terrain_set_index].terrains.size(); terrain_index++) { - p_list->push_back(PropertyInfo(Variant::STRING, vformat("terrain_set_%d/terrain_%d/name", terrain_set_index, terrain_index))); - p_list->push_back(PropertyInfo(Variant::COLOR, vformat("terrain_set_%d/terrain_%d/color", terrain_set_index, terrain_index))); - } +Vector TileSet::_get_square_corner_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit) { + Vector2 unit = Vector2(p_size) / 6.0; + Vector polygon; + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: + polygon.push_back(Vector2(0, 3) * unit); + polygon.push_back(Vector2(3, 3) * unit); + polygon.push_back(Vector2(3, 0) * unit); + polygon.push_back(Vector2(1, 0) * unit); + polygon.push_back(Vector2(1, 1) * unit); + polygon.push_back(Vector2(0, 1) * unit); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER: + polygon.push_back(Vector2(0, 3) * unit); + polygon.push_back(Vector2(-3, 3) * unit); + polygon.push_back(Vector2(-3, 0) * unit); + polygon.push_back(Vector2(-1, 0) * unit); + polygon.push_back(Vector2(-1, 1) * unit); + polygon.push_back(Vector2(0, 1) * unit); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER: + polygon.push_back(Vector2(0, -3) * unit); + polygon.push_back(Vector2(-3, -3) * unit); + polygon.push_back(Vector2(-3, 0) * unit); + polygon.push_back(Vector2(-1, 0) * unit); + polygon.push_back(Vector2(-1, -1) * unit); + polygon.push_back(Vector2(0, -1) * unit); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER: + polygon.push_back(Vector2(0, -3) * unit); + polygon.push_back(Vector2(3, -3) * unit); + polygon.push_back(Vector2(3, 0) * unit); + polygon.push_back(Vector2(1, 0) * unit); + polygon.push_back(Vector2(1, -1) * unit); + polygon.push_back(Vector2(0, -1) * unit); + break; + default: + break; } + return polygon; +} - // Navigation. - p_list->push_back(PropertyInfo(Variant::NIL, "Navigation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP)); - for (int i = 0; i < navigation_layers.size(); i++) { - p_list->push_back(PropertyInfo(Variant::INT, vformat("navigation_layer_%d/layers", i), PROPERTY_HINT_LAYERS_2D_NAVIGATION)); +Vector TileSet::_get_square_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit) { + Vector2 unit = Vector2(p_size) / 6.0; + Vector polygon; + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_RIGHT_SIDE: + polygon.push_back(Vector2(1, -1) * unit); + polygon.push_back(Vector2(3, -3) * unit); + polygon.push_back(Vector2(3, 3) * unit); + polygon.push_back(Vector2(1, 1) * unit); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE: + polygon.push_back(Vector2(-1, 1) * unit); + polygon.push_back(Vector2(-3, 3) * unit); + polygon.push_back(Vector2(3, 3) * unit); + polygon.push_back(Vector2(1, 1) * unit); + break; + case TileSet::CELL_NEIGHBOR_LEFT_SIDE: + polygon.push_back(Vector2(-1, -1) * unit); + polygon.push_back(Vector2(-3, -3) * unit); + polygon.push_back(Vector2(-3, 3) * unit); + polygon.push_back(Vector2(-1, 1) * unit); + break; + case TileSet::CELL_NEIGHBOR_TOP_SIDE: + polygon.push_back(Vector2(-1, -1) * unit); + polygon.push_back(Vector2(-3, -3) * unit); + polygon.push_back(Vector2(3, -3) * unit); + polygon.push_back(Vector2(1, -1) * unit); + break; + default: + break; } + return polygon; +} - // Custom data. - String argt = "Any"; - for (int i = 1; i < Variant::VARIANT_MAX; i++) { - argt += "," + Variant::get_type_name(Variant::Type(i)); - } - p_list->push_back(PropertyInfo(Variant::NIL, "Custom data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP)); - for (int i = 0; i < custom_data_layers.size(); i++) { - p_list->push_back(PropertyInfo(Variant::STRING, vformat("custom_data_layer_%d/name", i))); - p_list->push_back(PropertyInfo(Variant::INT, vformat("custom_data_layer_%d/type", i), PROPERTY_HINT_ENUM, argt)); +Vector TileSet::_get_isometric_corner_or_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit) { + Vector2 unit = Vector2(p_size) / 6.0; + Vector polygon; + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_RIGHT_CORNER: + polygon.push_back(Vector2(1, 0) * unit); + polygon.push_back(Vector2(2, -1) * unit); + polygon.push_back(Vector2(3, 0) * unit); + polygon.push_back(Vector2(2, 1) * unit); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE: + polygon.push_back(Vector2(0, 1) * unit); + polygon.push_back(Vector2(1, 2) * unit); + polygon.push_back(Vector2(2, 1) * unit); + polygon.push_back(Vector2(1, 0) * unit); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER: + polygon.push_back(Vector2(0, 1) * unit); + polygon.push_back(Vector2(-1, 2) * unit); + polygon.push_back(Vector2(0, 3) * unit); + polygon.push_back(Vector2(1, 2) * unit); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE: + polygon.push_back(Vector2(0, 1) * unit); + polygon.push_back(Vector2(-1, 2) * unit); + polygon.push_back(Vector2(-2, 1) * unit); + polygon.push_back(Vector2(-1, 0) * unit); + break; + case TileSet::CELL_NEIGHBOR_LEFT_CORNER: + polygon.push_back(Vector2(-1, 0) * unit); + polygon.push_back(Vector2(-2, -1) * unit); + polygon.push_back(Vector2(-3, 0) * unit); + polygon.push_back(Vector2(-2, 1) * unit); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE: + polygon.push_back(Vector2(0, -1) * unit); + polygon.push_back(Vector2(-1, -2) * unit); + polygon.push_back(Vector2(-2, -1) * unit); + polygon.push_back(Vector2(-1, 0) * unit); + break; + case TileSet::CELL_NEIGHBOR_TOP_CORNER: + polygon.push_back(Vector2(0, -1) * unit); + polygon.push_back(Vector2(-1, -2) * unit); + polygon.push_back(Vector2(0, -3) * unit); + polygon.push_back(Vector2(1, -2) * unit); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE: + polygon.push_back(Vector2(0, -1) * unit); + polygon.push_back(Vector2(1, -2) * unit); + polygon.push_back(Vector2(2, -1) * unit); + polygon.push_back(Vector2(1, 0) * unit); + break; + default: + break; } + return polygon; +} - // Sources. - // Note: sources have to be listed in at the end as some TileData rely on the TileSet properties being initialized first. - for (Map>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) { - p_list->push_back(PropertyInfo(Variant::INT, vformat("sources/%d", E_source->key()), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); +Vector TileSet::_get_isometric_corner_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit) { + Vector2 unit = Vector2(p_size) / 6.0; + Vector polygon; + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_RIGHT_CORNER: + polygon.push_back(Vector2(0.5, -0.5) * unit); + polygon.push_back(Vector2(1.5, -1.5) * unit); + polygon.push_back(Vector2(3, 0) * unit); + polygon.push_back(Vector2(1.5, 1.5) * unit); + polygon.push_back(Vector2(0.5, 0.5) * unit); + polygon.push_back(Vector2(1, 0) * unit); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER: + polygon.push_back(Vector2(-0.5, 0.5) * unit); + polygon.push_back(Vector2(-1.5, 1.5) * unit); + polygon.push_back(Vector2(0, 3) * unit); + polygon.push_back(Vector2(1.5, 1.5) * unit); + polygon.push_back(Vector2(0.5, 0.5) * unit); + polygon.push_back(Vector2(0, 1) * unit); + break; + case TileSet::CELL_NEIGHBOR_LEFT_CORNER: + polygon.push_back(Vector2(-0.5, -0.5) * unit); + polygon.push_back(Vector2(-1.5, -1.5) * unit); + polygon.push_back(Vector2(-3, 0) * unit); + polygon.push_back(Vector2(-1.5, 1.5) * unit); + polygon.push_back(Vector2(-0.5, 0.5) * unit); + polygon.push_back(Vector2(-1, 0) * unit); + break; + case TileSet::CELL_NEIGHBOR_TOP_CORNER: + polygon.push_back(Vector2(-0.5, -0.5) * unit); + polygon.push_back(Vector2(-1.5, -1.5) * unit); + polygon.push_back(Vector2(0, -3) * unit); + polygon.push_back(Vector2(1.5, -1.5) * unit); + polygon.push_back(Vector2(0.5, -0.5) * unit); + polygon.push_back(Vector2(0, -1) * unit); + break; + default: + break; } + return polygon; } -void TileSet::_bind_methods() { - // Sources management. - ClassDB::bind_method(D_METHOD("get_next_source_id"), &TileSet::get_next_source_id); - ClassDB::bind_method(D_METHOD("add_source", "atlas_source_id_override"), &TileSet::add_source, DEFVAL(-1)); - ClassDB::bind_method(D_METHOD("remove_source", "source_id"), &TileSet::remove_source); - ClassDB::bind_method(D_METHOD("set_source_id", "source_id"), &TileSet::set_source_id); - ClassDB::bind_method(D_METHOD("get_source_count"), &TileSet::get_source_count); - ClassDB::bind_method(D_METHOD("get_source_id", "index"), &TileSet::get_source_id); - ClassDB::bind_method(D_METHOD("has_source", "index"), &TileSet::has_source); - ClassDB::bind_method(D_METHOD("get_source", "index"), &TileSet::get_source); - - // Shape and layout. - ClassDB::bind_method(D_METHOD("set_tile_shape", "shape"), &TileSet::set_tile_shape); - ClassDB::bind_method(D_METHOD("get_tile_shape"), &TileSet::get_tile_shape); - ClassDB::bind_method(D_METHOD("set_tile_layout", "layout"), &TileSet::set_tile_layout); - ClassDB::bind_method(D_METHOD("get_tile_layout"), &TileSet::get_tile_layout); - ClassDB::bind_method(D_METHOD("set_tile_offset_axis", "alignment"), &TileSet::set_tile_offset_axis); - ClassDB::bind_method(D_METHOD("get_tile_offset_axis"), &TileSet::get_tile_offset_axis); - ClassDB::bind_method(D_METHOD("set_tile_size", "size"), &TileSet::set_tile_size); - ClassDB::bind_method(D_METHOD("get_tile_size"), &TileSet::get_tile_size); - ClassDB::bind_method(D_METHOD("set_tile_skew", "skew"), &TileSet::set_tile_skew); - ClassDB::bind_method(D_METHOD("get_tile_skew"), &TileSet::get_tile_skew); - - ADD_PROPERTY(PropertyInfo(Variant::INT, "tile_shape", PROPERTY_HINT_ENUM, "Square,Isometric,Half-Offset Square,Hexagon"), "set_tile_shape", "get_tile_shape"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "tile_layout", PROPERTY_HINT_ENUM, "Stacked,Stacked Offset,Stairs Right,Stairs Down,Diamond Right,Diamond Down"), "set_tile_layout", "get_tile_layout"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "tile_offset_axis", PROPERTY_HINT_ENUM, "Horizontal Offset,Vertical Offset"), "set_tile_offset_axis", "get_tile_offset_axis"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "tile_size"), "set_tile_size", "get_tile_size"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "tile_skew"), "set_tile_skew", "get_tile_skew"); - - // Rendering. - ClassDB::bind_method(D_METHOD("set_uv_clipping", "uv_clipping"), &TileSet::set_uv_clipping); - ClassDB::bind_method(D_METHOD("is_uv_clipping"), &TileSet::is_uv_clipping); - ClassDB::bind_method(D_METHOD("set_y_sorting", "y_sorting"), &TileSet::set_y_sorting); - ClassDB::bind_method(D_METHOD("is_y_sorting"), &TileSet::is_y_sorting); - - ClassDB::bind_method(D_METHOD("set_occlusion_layers_count", "occlusion_layers_count"), &TileSet::set_occlusion_layers_count); - ClassDB::bind_method(D_METHOD("get_occlusion_layers_count"), &TileSet::get_occlusion_layers_count); - ClassDB::bind_method(D_METHOD("set_occlusion_layer_light_mask", "layer_index", "light_mask"), &TileSet::set_occlusion_layer_light_mask); - ClassDB::bind_method(D_METHOD("get_occlusion_layer_light_mask"), &TileSet::get_occlusion_layer_light_mask); - ClassDB::bind_method(D_METHOD("set_occlusion_layer_sdf_collision", "layer_index", "sdf_collision"), &TileSet::set_occlusion_layer_sdf_collision); - ClassDB::bind_method(D_METHOD("get_occlusion_layer_sdf_collision"), &TileSet::get_occlusion_layer_sdf_collision); - - // Physics - ClassDB::bind_method(D_METHOD("set_physics_layers_count", "physics_layers_count"), &TileSet::set_physics_layers_count); - ClassDB::bind_method(D_METHOD("get_physics_layers_count"), &TileSet::get_physics_layers_count); - ClassDB::bind_method(D_METHOD("set_physics_layer_collision_layer", "layer_index", "layer"), &TileSet::set_physics_layer_collision_layer); - ClassDB::bind_method(D_METHOD("get_physics_layer_collision_layer", "layer_index"), &TileSet::get_physics_layer_collision_layer); - ClassDB::bind_method(D_METHOD("set_physics_layer_collision_mask", "layer_index", "mask"), &TileSet::set_physics_layer_collision_mask); - ClassDB::bind_method(D_METHOD("get_physics_layer_collision_mask", "layer_index"), &TileSet::get_physics_layer_collision_mask); - ClassDB::bind_method(D_METHOD("set_physics_layer_physics_material", "layer_index", "physics_material"), &TileSet::set_physics_layer_physics_material); - ClassDB::bind_method(D_METHOD("get_physics_layer_physics_material", "layer_index"), &TileSet::get_physics_layer_physics_material); - - // Terrains - ClassDB::bind_method(D_METHOD("set_terrain_sets_count", "terrain_sets_count"), &TileSet::set_terrain_sets_count); - ClassDB::bind_method(D_METHOD("get_terrain_sets_count"), &TileSet::get_terrain_sets_count); - ClassDB::bind_method(D_METHOD("set_terrain_set_mode", "terrain_set", "mode"), &TileSet::set_terrain_set_mode); - ClassDB::bind_method(D_METHOD("get_terrain_set_mode", "terrain_set"), &TileSet::get_terrain_set_mode); - - ClassDB::bind_method(D_METHOD("set_terrains_count", "terrain_set", "terrains_count"), &TileSet::set_terrains_count); - ClassDB::bind_method(D_METHOD("get_terrains_count", "terrain_set"), &TileSet::get_terrains_count); - ClassDB::bind_method(D_METHOD("set_terrain_name", "terrain_set", "terrain_index", "name"), &TileSet::set_terrain_name); - ClassDB::bind_method(D_METHOD("get_terrain_name", "terrain_set", "terrain_index"), &TileSet::get_terrain_name); - ClassDB::bind_method(D_METHOD("set_terrain_color", "terrain_set", "terrain_index", "color"), &TileSet::set_terrain_color); - ClassDB::bind_method(D_METHOD("get_terrain_color", "terrain_set", "terrain_index"), &TileSet::get_terrain_color); - - // Navigation - ClassDB::bind_method(D_METHOD("set_navigation_layers_count", "navigation_layers_count"), &TileSet::set_navigation_layers_count); - ClassDB::bind_method(D_METHOD("get_navigation_layers_count"), &TileSet::get_navigation_layers_count); - ClassDB::bind_method(D_METHOD("set_navigation_layer_layers", "layer_index", "layers"), &TileSet::set_navigation_layer_layers); - ClassDB::bind_method(D_METHOD("get_navigation_layer_layers", "layer_index"), &TileSet::get_navigation_layer_layers); - - // Custom data - ClassDB::bind_method(D_METHOD("set_custom_data_layers_count", "custom_data_layers_count"), &TileSet::set_custom_data_layers_count); - ClassDB::bind_method(D_METHOD("get_custom_data_layers_count"), &TileSet::get_custom_data_layers_count); - - ADD_GROUP("Rendering", ""); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "uv_clipping"), "set_uv_clipping", "is_uv_clipping"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "y_sorting"), "set_y_sorting", "is_y_sorting"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "occlusion_layers_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_occlusion_layers_count", "get_occlusion_layers_count"); - - ADD_GROUP("Physics", ""); - ADD_PROPERTY(PropertyInfo(Variant::INT, "physics_layers_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_physics_layers_count", "get_physics_layers_count"); - - ADD_GROUP("Terrains", ""); - ADD_PROPERTY(PropertyInfo(Variant::INT, "terrains_sets_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_terrain_sets_count", "get_terrain_sets_count"); - - ADD_GROUP("Navigation", ""); - ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_layers_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_navigation_layers_count", "get_navigation_layers_count"); - - ADD_GROUP("Custom data", ""); - ADD_PROPERTY(PropertyInfo(Variant::INT, "custom_data_layers_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_custom_data_layers_count", "get_custom_data_layers_count"); +Vector TileSet::_get_isometric_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit) { + Vector2 unit = Vector2(p_size) / 6.0; + Vector polygon; + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE: + polygon.push_back(Vector2(1, 0) * unit); + polygon.push_back(Vector2(3, 0) * unit); + polygon.push_back(Vector2(0, 3) * unit); + polygon.push_back(Vector2(0, 1) * unit); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE: + polygon.push_back(Vector2(-1, 0) * unit); + polygon.push_back(Vector2(-3, 0) * unit); + polygon.push_back(Vector2(0, 3) * unit); + polygon.push_back(Vector2(0, 1) * unit); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE: + polygon.push_back(Vector2(-1, 0) * unit); + polygon.push_back(Vector2(-3, 0) * unit); + polygon.push_back(Vector2(0, -3) * unit); + polygon.push_back(Vector2(0, -1) * unit); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE: + polygon.push_back(Vector2(1, 0) * unit); + polygon.push_back(Vector2(3, 0) * unit); + polygon.push_back(Vector2(0, -3) * unit); + polygon.push_back(Vector2(0, -1) * unit); + break; + default: + break; + } + return polygon; +} - // -- Enum binding -- - BIND_ENUM_CONSTANT(TILE_SHAPE_SQUARE); - BIND_ENUM_CONSTANT(TILE_SHAPE_ISOMETRIC); - BIND_ENUM_CONSTANT(TILE_SHAPE_HALF_OFFSET_SQUARE); - BIND_ENUM_CONSTANT(TILE_SHAPE_HEXAGON); +Vector TileSet::_get_half_offset_corner_or_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis) { + Vector point_list; + point_list.push_back(Vector2(3, (3.0 * (1.0 - p_overlap * 2.0)) / 2.0)); + point_list.push_back(Vector2(3, 3.0 * (1.0 - p_overlap * 2.0))); + point_list.push_back(Vector2(2, 3.0 * (1.0 - (p_overlap * 2.0) * 2.0 / 3.0))); + point_list.push_back(Vector2(1, 3.0 - p_overlap * 2.0)); + point_list.push_back(Vector2(0, 3)); + point_list.push_back(Vector2(-1, 3.0 - p_overlap * 2.0)); + point_list.push_back(Vector2(-2, 3.0 * (1.0 - (p_overlap * 2.0) * 2.0 / 3.0))); + point_list.push_back(Vector2(-3, 3.0 * (1.0 - p_overlap * 2.0))); + point_list.push_back(Vector2(-3, (3.0 * (1.0 - p_overlap * 2.0)) / 2.0)); + point_list.push_back(Vector2(-3, -(3.0 * (1.0 - p_overlap * 2.0)) / 2.0)); + point_list.push_back(Vector2(-3, -3.0 * (1.0 - p_overlap * 2.0))); + point_list.push_back(Vector2(-2, -3.0 * (1.0 - (p_overlap * 2.0) * 2.0 / 3.0))); + point_list.push_back(Vector2(-1, -(3.0 - p_overlap * 2.0))); + point_list.push_back(Vector2(0, -3)); + point_list.push_back(Vector2(1, -(3.0 - p_overlap * 2.0))); + point_list.push_back(Vector2(2, -3.0 * (1.0 - (p_overlap * 2.0) * 2.0 / 3.0))); + point_list.push_back(Vector2(3, -3.0 * (1.0 - p_overlap * 2.0))); + point_list.push_back(Vector2(3, -(3.0 * (1.0 - p_overlap * 2.0)) / 2.0)); - BIND_ENUM_CONSTANT(TILE_LAYOUT_STACKED); - BIND_ENUM_CONSTANT(TILE_LAYOUT_STACKED_OFFSET); - BIND_ENUM_CONSTANT(TILE_LAYOUT_STAIRS_RIGHT); - BIND_ENUM_CONSTANT(TILE_LAYOUT_STAIRS_DOWN); - BIND_ENUM_CONSTANT(TILE_LAYOUT_DIAMOND_RIGHT); - BIND_ENUM_CONSTANT(TILE_LAYOUT_DIAMOND_DOWN); + Vector2 unit = Vector2(p_size) / 6.0; + for (int i = 0; i < point_list.size(); i++) { + point_list.write[i] = point_list[i] * unit; + } - BIND_ENUM_CONSTANT(TILE_OFFSET_AXIS_HORIZONTAL); - BIND_ENUM_CONSTANT(TILE_OFFSET_AXIS_VERTICAL); + Vector polygon; + if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_RIGHT_SIDE: + polygon.push_back(point_list[17]); + polygon.push_back(point_list[0]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: + polygon.push_back(point_list[0]); + polygon.push_back(point_list[1]); + polygon.push_back(point_list[2]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE: + polygon.push_back(point_list[2]); + polygon.push_back(point_list[3]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER: + polygon.push_back(point_list[3]); + polygon.push_back(point_list[4]); + polygon.push_back(point_list[5]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE: + polygon.push_back(point_list[5]); + polygon.push_back(point_list[6]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER: + polygon.push_back(point_list[6]); + polygon.push_back(point_list[7]); + polygon.push_back(point_list[8]); + break; + case TileSet::CELL_NEIGHBOR_LEFT_SIDE: + polygon.push_back(point_list[8]); + polygon.push_back(point_list[9]); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER: + polygon.push_back(point_list[9]); + polygon.push_back(point_list[10]); + polygon.push_back(point_list[11]); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE: + polygon.push_back(point_list[11]); + polygon.push_back(point_list[12]); + break; + case TileSet::CELL_NEIGHBOR_TOP_CORNER: + polygon.push_back(point_list[12]); + polygon.push_back(point_list[13]); + polygon.push_back(point_list[14]); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE: + polygon.push_back(point_list[14]); + polygon.push_back(point_list[15]); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER: + polygon.push_back(point_list[15]); + polygon.push_back(point_list[16]); + polygon.push_back(point_list[17]); + break; + default: + break; + } + } else { + if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL) { + for (int i = 0; i < point_list.size(); i++) { + point_list.write[i] = Vector2(point_list[i].y, point_list[i].x); + } + } + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_RIGHT_CORNER: + polygon.push_back(point_list[3]); + polygon.push_back(point_list[4]); + polygon.push_back(point_list[5]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE: + polygon.push_back(point_list[2]); + polygon.push_back(point_list[3]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: + polygon.push_back(point_list[0]); + polygon.push_back(point_list[1]); + polygon.push_back(point_list[2]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE: + polygon.push_back(point_list[17]); + polygon.push_back(point_list[0]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER: + polygon.push_back(point_list[15]); + polygon.push_back(point_list[16]); + polygon.push_back(point_list[17]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE: + polygon.push_back(point_list[14]); + polygon.push_back(point_list[15]); + break; + case TileSet::CELL_NEIGHBOR_LEFT_CORNER: + polygon.push_back(point_list[12]); + polygon.push_back(point_list[13]); + polygon.push_back(point_list[14]); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE: + polygon.push_back(point_list[11]); + polygon.push_back(point_list[12]); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER: + polygon.push_back(point_list[9]); + polygon.push_back(point_list[10]); + polygon.push_back(point_list[11]); + break; + case TileSet::CELL_NEIGHBOR_TOP_SIDE: + polygon.push_back(point_list[8]); + polygon.push_back(point_list[9]); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER: + polygon.push_back(point_list[6]); + polygon.push_back(point_list[7]); + polygon.push_back(point_list[8]); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE: + polygon.push_back(point_list[5]); + polygon.push_back(point_list[6]); + break; + default: + break; + } + } - BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_RIGHT_SIDE); - BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_RIGHT_CORNER); - BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE); - BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER); - BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_BOTTOM_SIDE); - BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_BOTTOM_CORNER); - BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE); - BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER); - BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_LEFT_SIDE); - BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_LEFT_CORNER); - BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE); - BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER); - BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_TOP_SIDE); - BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_TOP_CORNER); - BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE); - BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER); + int half_polygon_size = polygon.size(); + for (int i = 0; i < half_polygon_size; i++) { + polygon.push_back(polygon[half_polygon_size - 1 - i] / 3.0); + } - BIND_ENUM_CONSTANT(TERRAIN_MODE_MATCH_CORNERS_AND_SIDES); - BIND_ENUM_CONSTANT(TERRAIN_MODE_MATCH_CORNERS); - BIND_ENUM_CONSTANT(TERRAIN_MODE_MATCH_SIDES); + return polygon; } -TileSet::TileSet() { - // Instanciatie and list all plugins. - tile_set_plugins_vector.append(memnew(TileSetPluginAtlasRendering)); - tile_set_plugins_vector.append(memnew(TileSetPluginAtlasPhysics)); - tile_set_plugins_vector.append(memnew(TileSetPluginAtlasTerrain)); - tile_set_plugins_vector.append(memnew(TileSetPluginAtlasNavigation)); - tile_set_plugins_vector.append(memnew(TileSetPluginScenesCollections)); -} +Vector TileSet::_get_half_offset_corner_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis) { + Vector point_list; + point_list.push_back(Vector2(3, 0)); + point_list.push_back(Vector2(3, 3.0 * (1.0 - p_overlap * 2.0))); + point_list.push_back(Vector2(1.5, (3.0 * (1.0 - p_overlap * 2.0) + 3.0) / 2.0)); + point_list.push_back(Vector2(0, 3)); + point_list.push_back(Vector2(-1.5, (3.0 * (1.0 - p_overlap * 2.0) + 3.0) / 2.0)); + point_list.push_back(Vector2(-3, 3.0 * (1.0 - p_overlap * 2.0))); + point_list.push_back(Vector2(-3, 0)); + point_list.push_back(Vector2(-3, -3.0 * (1.0 - p_overlap * 2.0))); + point_list.push_back(Vector2(-1.5, -(3.0 * (1.0 - p_overlap * 2.0) + 3.0) / 2.0)); + point_list.push_back(Vector2(0, -3)); + point_list.push_back(Vector2(1.5, -(3.0 * (1.0 - p_overlap * 2.0) + 3.0) / 2.0)); + point_list.push_back(Vector2(3, -3.0 * (1.0 - p_overlap * 2.0))); -TileSet::~TileSet() { -#ifndef DISABLE_DEPRECATED - for (Map::Element *E = compatibility_data.front(); E; E = E->next()) { - memdelete(E->get()); - } -#endif // DISABLE_DEPRECATED - while (!source_ids.is_empty()) { - remove_source(source_ids[0]); + Vector2 unit = Vector2(p_size) / 6.0; + for (int i = 0; i < point_list.size(); i++) { + point_list.write[i] = point_list[i] * unit; } - for (int i = 0; i < tile_set_plugins_vector.size(); i++) { - memdelete(tile_set_plugins_vector[i]); + + Vector polygon; + if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: + polygon.push_back(point_list[0]); + polygon.push_back(point_list[1]); + polygon.push_back(point_list[2]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER: + polygon.push_back(point_list[2]); + polygon.push_back(point_list[3]); + polygon.push_back(point_list[4]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER: + polygon.push_back(point_list[4]); + polygon.push_back(point_list[5]); + polygon.push_back(point_list[6]); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER: + polygon.push_back(point_list[6]); + polygon.push_back(point_list[7]); + polygon.push_back(point_list[8]); + break; + case TileSet::CELL_NEIGHBOR_TOP_CORNER: + polygon.push_back(point_list[8]); + polygon.push_back(point_list[9]); + polygon.push_back(point_list[10]); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER: + polygon.push_back(point_list[10]); + polygon.push_back(point_list[11]); + polygon.push_back(point_list[0]); + break; + default: + break; + } + } else { + if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL) { + for (int i = 0; i < point_list.size(); i++) { + point_list.write[i] = Vector2(point_list[i].y, point_list[i].x); + } + } + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_RIGHT_CORNER: + polygon.push_back(point_list[2]); + polygon.push_back(point_list[3]); + polygon.push_back(point_list[4]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: + polygon.push_back(point_list[0]); + polygon.push_back(point_list[1]); + polygon.push_back(point_list[2]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER: + polygon.push_back(point_list[10]); + polygon.push_back(point_list[11]); + polygon.push_back(point_list[0]); + break; + case TileSet::CELL_NEIGHBOR_LEFT_CORNER: + polygon.push_back(point_list[8]); + polygon.push_back(point_list[9]); + polygon.push_back(point_list[10]); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER: + polygon.push_back(point_list[6]); + polygon.push_back(point_list[7]); + polygon.push_back(point_list[8]); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER: + polygon.push_back(point_list[4]); + polygon.push_back(point_list[5]); + polygon.push_back(point_list[6]); + break; + default: + break; + } } -} -/////////////////////////////// TileSetSource ////////////////////////////////////// + int half_polygon_size = polygon.size(); + for (int i = 0; i < half_polygon_size; i++) { + polygon.push_back(polygon[half_polygon_size - 1 - i] / 3.0); + } -void TileSetSource::set_tile_set(const TileSet *p_tile_set) { - tile_set = p_tile_set; + return polygon; } -/////////////////////////////// TileSetAtlasSource ////////////////////////////////////// - -void TileSetAtlasSource::set_tile_set(const TileSet *p_tile_set) { - tile_set = p_tile_set; +Vector TileSet::_get_half_offset_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis) { + Vector point_list; + point_list.push_back(Vector2(3, 3.0 * (1.0 - p_overlap * 2.0))); + point_list.push_back(Vector2(0, 3)); + point_list.push_back(Vector2(-3, 3.0 * (1.0 - p_overlap * 2.0))); + point_list.push_back(Vector2(-3, -3.0 * (1.0 - p_overlap * 2.0))); + point_list.push_back(Vector2(0, -3)); + point_list.push_back(Vector2(3, -3.0 * (1.0 - p_overlap * 2.0))); - // Set the TileSet on all TileData. - for (Map::Element *E_tile = tiles.front(); E_tile; E_tile = E_tile->next()) { - for (Map::Element *E_alternative = E_tile->get().alternatives.front(); E_alternative; E_alternative = E_alternative->next()) { - E_alternative->get()->set_tile_set(tile_set); - } + Vector2 unit = Vector2(p_size) / 6.0; + for (int i = 0; i < point_list.size(); i++) { + point_list.write[i] = point_list[i] * unit; } -} -void TileSetAtlasSource::notify_tile_data_properties_should_change() { - // Set the TileSet on all TileData. - for (Map::Element *E_tile = tiles.front(); E_tile; E_tile = E_tile->next()) { - for (Map::Element *E_alternative = E_tile->get().alternatives.front(); E_alternative; E_alternative = E_alternative->next()) { - E_alternative->get()->notify_tile_data_properties_should_change(); + Vector polygon; + if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_RIGHT_SIDE: + polygon.push_back(point_list[5]); + polygon.push_back(point_list[0]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE: + polygon.push_back(point_list[0]); + polygon.push_back(point_list[1]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE: + polygon.push_back(point_list[1]); + polygon.push_back(point_list[2]); + break; + case TileSet::CELL_NEIGHBOR_LEFT_SIDE: + polygon.push_back(point_list[2]); + polygon.push_back(point_list[3]); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE: + polygon.push_back(point_list[3]); + polygon.push_back(point_list[4]); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE: + polygon.push_back(point_list[4]); + polygon.push_back(point_list[5]); + break; + default: + break; } - } -} - -void TileSetAtlasSource::reset_state() { - // Reset all TileData. - for (Map::Element *E_tile = tiles.front(); E_tile; E_tile = E_tile->next()) { - for (Map::Element *E_alternative = E_tile->get().alternatives.front(); E_alternative; E_alternative = E_alternative->next()) { - E_alternative->get()->reset_state(); + } else { + if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL) { + for (int i = 0; i < point_list.size(); i++) { + point_list.write[i] = Vector2(point_list[i].y, point_list[i].x); + } + } + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE: + polygon.push_back(point_list[0]); + polygon.push_back(point_list[1]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE: + polygon.push_back(point_list[5]); + polygon.push_back(point_list[0]); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE: + polygon.push_back(point_list[4]); + polygon.push_back(point_list[5]); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE: + polygon.push_back(point_list[3]); + polygon.push_back(point_list[4]); + break; + case TileSet::CELL_NEIGHBOR_TOP_SIDE: + polygon.push_back(point_list[2]); + polygon.push_back(point_list[3]); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE: + polygon.push_back(point_list[1]); + polygon.push_back(point_list[2]); + break; + default: + break; } } -} - -void TileSetAtlasSource::set_texture(Ref p_texture) { - texture = p_texture; - emit_changed(); -} - -Ref TileSetAtlasSource::get_texture() const { - return texture; -} - -void TileSetAtlasSource::set_margins(Vector2i p_margins) { - if (p_margins.x < 0 || p_margins.y < 0) { - WARN_PRINT("Atlas source margins should be positive."); - margins = Vector2i(MAX(0, p_margins.x), MAX(0, p_margins.y)); - } else { - margins = p_margins; + int half_polygon_size = polygon.size(); + for (int i = 0; i < half_polygon_size; i++) { + polygon.push_back(polygon[half_polygon_size - 1 - i] / 3.0); } - emit_changed(); + return polygon; } -Vector2i TileSetAtlasSource::get_margins() const { - return margins; + +void TileSet::reset_state() { + occlusion_layers.clear(); + physics_layers.clear(); + custom_data_layers.clear(); } -void TileSetAtlasSource::set_separation(Vector2i p_separation) { - if (p_separation.x < 0 || p_separation.y < 0) { - WARN_PRINT("Atlas source separation should be positive."); - separation = Vector2i(MAX(0, p_separation.x), MAX(0, p_separation.y)); - } else { - separation = p_separation; - } +const Vector2i TileSetSource::INVALID_ATLAS_COORDS = Vector2i(-1, -1); +const int TileSetSource::INVALID_TILE_ALTERNATIVE = -1; - emit_changed(); -} -Vector2i TileSetAtlasSource::get_separation() const { - return separation; -} +#ifndef DISABLE_DEPRECATED +void TileSet::compatibility_conversion() { + for (Map::Element *E = compatibility_data.front(); E; E = E->next()) { + CompatibilityTileData *ctd = E->value(); -void TileSetAtlasSource::set_texture_region_size(Vector2i p_tile_size) { - if (p_tile_size.x <= 0 || p_tile_size.y <= 0) { - WARN_PRINT("Atlas source tile_size should be strictly positive."); - texture_region_size = Vector2i(MAX(1, p_tile_size.x), MAX(1, p_tile_size.y)); - } else { - texture_region_size = p_tile_size; - } + // Add the texture + TileSetAtlasSource *atlas_source = memnew(TileSetAtlasSource); + int source_id = add_source(Ref(atlas_source)); - emit_changed(); -} -Vector2i TileSetAtlasSource::get_texture_region_size() const { - return texture_region_size; -} + atlas_source->set_texture(ctd->texture); -Vector2i TileSetAtlasSource::get_atlas_grid_size() const { - Ref texture = get_texture(); - if (!texture.is_valid()) { - return Vector2i(); - } + // Handle each tile as a new source. Not optimal but at least it should stay compatible. + switch (ctd->tile_mode) { + case 0: // SINGLE_TILE + // TODO + break; + case 1: // AUTO_TILE + // TODO + break; + case 2: // ATLAS_TILE + atlas_source->set_margins(ctd->region.get_position()); + atlas_source->set_separation(Vector2i(ctd->autotile_spacing, ctd->autotile_spacing)); + atlas_source->set_texture_region_size(ctd->autotile_tile_size); - ERR_FAIL_COND_V(texture_region_size.x <= 0 || texture_region_size.y <= 0, Vector2i()); + Size2i atlas_size = ctd->region.get_size() / (ctd->autotile_tile_size + atlas_source->get_separation()); + for (int i = 0; i < atlas_size.x; i++) { + for (int j = 0; j < atlas_size.y; j++) { + Vector2i coords = Vector2i(i, j); - Size2i valid_area = texture->get_size() - margins; + for (int flags = 0; flags < 8; flags++) { + bool flip_h = flags & 1; + bool flip_v = flags & 2; + bool transpose = flags & 4; - // Compute the number of valid tiles in the tiles atlas - Size2i grid_size = Size2i(); - if (valid_area.x >= texture_region_size.x && valid_area.y >= texture_region_size.y) { - valid_area -= texture_region_size; - grid_size = Size2i(1, 1) + valid_area / (texture_region_size + separation); - } - return grid_size; -} + int alternative_tile = 0; + if (!atlas_source->has_tile(coords)) { + atlas_source->create_tile(coords); + } else { + alternative_tile = atlas_source->create_alternative_tile(coords); + } + TileData *tile_data = Object::cast_to(atlas_source->get_tile_data(coords, alternative_tile)); -bool TileSetAtlasSource::_set(const StringName &p_name, const Variant &p_value) { - Vector components = String(p_name).split("/", true, 2); + tile_data->set_flip_h(flip_h); + tile_data->set_flip_v(flip_v); + tile_data->set_transpose(transpose); + tile_data->tile_set_material(ctd->material); + tile_data->set_modulate(ctd->modulate); + tile_data->set_z_index(ctd->z_index); + if (ctd->autotile_occluder_map.has(coords)) { + if (get_occlusion_layers_count() < 1) { + set_occlusion_layers_count(1); + } + tile_data->set_occluder(0, ctd->autotile_occluder_map[coords]); + } + if (ctd->autotile_navpoly_map.has(coords)) { + if (get_navigation_layers_count() < 1) { + set_navigation_layers_count(1); + } + tile_data->set_navigation_polygon(0, ctd->autotile_navpoly_map[coords]); + } + if (ctd->autotile_priority_map.has(coords)) { + tile_data->set_probability(ctd->autotile_priority_map[coords]); + } + if (ctd->autotile_z_index_map.has(coords)) { + tile_data->set_z_index(ctd->autotile_z_index_map[coords]); + } - // Compute the vector2i if we have coordinates. - Vector coords_split = components[0].split(":"); - Vector2i coords = TileSetSource::INVALID_ATLAS_COORDS; - if (coords_split.size() == 2 && coords_split[0].is_valid_int() && coords_split[1].is_valid_int()) { - coords = Vector2i(coords_split[0].to_int(), coords_split[1].to_int()); - } + // Add the shapes. + if (ctd->shapes.size() > 0) { + if (get_physics_layers_count() < 1) { + set_physics_layers_count(1); + } + } + for (int k = 0; k < ctd->shapes.size(); k++) { + CompatibilityShapeData csd = ctd->shapes[k]; + if (csd.autotile_coords == coords) { + Ref convex_shape = csd.shape; // Only ConvexPolygonShape2D are supported, which is the default type used by the 3.x editor + if (convex_shape.is_valid()) { + Vector polygon = convex_shape->get_points(); + for (int point_index = 0; point_index < polygon.size(); point_index++) { + polygon.write[point_index] = csd.transform.xform(polygon[point_index]); + } + tile_data->set_collision_polygons_count(0, tile_data->get_collision_polygons_count(0) + 1); + int index = tile_data->get_collision_polygons_count(0) - 1; + tile_data->set_collision_polygon_one_way(0, index, csd.one_way); + tile_data->set_collision_polygon_one_way_margin(0, index, csd.one_way_margin); + tile_data->set_collision_polygon_points(0, index, polygon); + } + } + } - // Properties. - if (coords != TileSetSource::INVALID_ATLAS_COORDS) { - // Create the tile if needed. - if (!has_tile(coords)) { - create_tile(coords); - } - if (components.size() >= 2) { - // Properties. - if (components[1] == "size_in_atlas") { - move_tile_in_atlas(coords, coords, p_value); - } else if (components[1] == "next_alternative_id") { - tiles[coords].next_alternative_id = p_value; - } else if (components[1].is_valid_int()) { - int alternative_id = components[1].to_int(); - if (alternative_id != TileSetSource::INVALID_TILE_ALTERNATIVE) { - // Create the alternative if needed ? - if (!has_alternative_tile(coords, alternative_id)) { - create_alternative_tile(coords, alternative_id); - } - if (!tiles[coords].alternatives.has(alternative_id)) { - tiles[coords].alternatives[alternative_id] = memnew(TileData); - tiles[coords].alternatives[alternative_id]->set_tile_set(tile_set); - tiles[coords].alternatives[alternative_id]->set_allow_transform(alternative_id > 0); - tiles[coords].alternatives_ids.append(alternative_id); - } - if (components.size() >= 3) { - bool valid; - tiles[coords].alternatives[alternative_id]->set(components[2], p_value, &valid); - return valid; - } else { - // Only create the alternative if it did not exist yet. - return true; + // -- TODO: handle -- + // Those are offset for the whole atlas, they are likely useless for the atlases, but might make sense for single tiles. + // texture offset + // occluder_offset + // navigation_offset + + // For terrains, ignored for now? + // bitmask_mode + // bitmask_flags + } } } - } + break; } - } - - return false; -} - -bool TileSetAtlasSource::_get(const StringName &p_name, Variant &r_ret) const { - Vector components = String(p_name).split("/", true, 2); - // Properties. - Vector coords_split = components[0].split(":"); - if (coords_split.size() == 2 && coords_split[0].is_valid_int() && coords_split[1].is_valid_int()) { - Vector2i coords = Vector2i(coords_split[0].to_int(), coords_split[1].to_int()); - if (tiles.has(coords)) { - if (components.size() >= 2) { - // Properties. - if (components[1] == "size_in_atlas") { - r_ret = tiles[coords].size_in_atlas; - return true; - } else if (components[1] == "next_alternative_id") { - r_ret = tiles[coords].next_alternative_id; - return true; - } else if (components[1].is_valid_int()) { - int alternative_id = components[1].to_int(); - if (alternative_id != TileSetSource::INVALID_TILE_ALTERNATIVE && tiles[coords].alternatives.has(alternative_id)) { - if (components.size() >= 3) { - bool valid; - r_ret = tiles[coords].alternatives[alternative_id]->get(components[2], &valid); - return valid; - } else { - // Only to notify the tile alternative exists. - r_ret = alternative_id; - return true; - } - } + // Offset all shapes + for (int k = 0; k < ctd->shapes.size(); k++) { + Ref convex = ctd->shapes[k].shape; + if (convex.is_valid()) { + Vector points = convex->get_points(); + for (int i_point = 0; i_point < points.size(); i_point++) { + points.write[i_point] = points[i_point] - get_tile_size() / 2; } + convex->set_points(points); } } + + // Add the mapping to the map + compatibility_source_mapping.insert(E->key(), source_id); } - return false; + // Reset compatibility data + for (Map::Element *E = compatibility_data.front(); E; E = E->next()) { + memdelete(E->get()); + } + compatibility_data = Map(); } +#endif // DISABLE_DEPRECATED -void TileSetAtlasSource::_get_property_list(List *p_list) const { - // Atlases data. - PropertyInfo property_info; - for (Map::Element *E_tile = tiles.front(); E_tile; E_tile = E_tile->next()) { - List tile_property_list; +bool TileSet::_set(const StringName &p_name, const Variant &p_value) { + Vector components = String(p_name).split("/", true, 2); - // size_in_atlas - property_info = PropertyInfo(Variant::VECTOR2I, "size_in_atlas", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR); - if (E_tile->get().size_in_atlas == Vector2i(1, 1)) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; +#ifndef DISABLE_DEPRECATED + // TODO: THIS IS HOW WE CHECK IF WE HAVE A DEPRECATED RESOURCE + // This should be moved to a dedicated conversion system + if (components.size() >= 1 && components[0].is_valid_int()) { + int id = components[0].to_int(); + + // Get or create the compatibility object + CompatibilityTileData *ctd; + Map::Element *E = compatibility_data.find(id); + if (!E) { + ctd = memnew(CompatibilityTileData); + compatibility_data.insert(id, ctd); + } else { + ctd = E->get(); } - tile_property_list.push_back(property_info); - // next_alternative_id - property_info = PropertyInfo(Variant::INT, "next_alternative_id", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR); - if (E_tile->get().next_alternative_id == 1) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; + if (components.size() < 2) { + return false; } - tile_property_list.push_back(property_info); - for (Map::Element *E_alternative = E_tile->get().alternatives.front(); E_alternative; E_alternative = E_alternative->next()) { - // Add a dummy property to show the alternative exists. - tile_property_list.push_back(PropertyInfo(Variant::INT, vformat("%d", E_alternative->key()), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); + String what = components[1]; - // Get the alternative tile's properties and append them to the list of properties. - List alternative_property_list; - E_alternative->get()->get_property_list(&alternative_property_list); - for (List::Element *E_property = alternative_property_list.front(); E_property; E_property = E_property->next()) { - property_info = E_property->get(); - bool valid; - Variant default_value = ClassDB::class_get_default_property_value("TileData", property_info.name, &valid); - Variant value = E_alternative->get()->get(property_info.name); - if (valid && value == default_value) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; + if (what == "name") { + ctd->name = p_value; + } else if (what == "texture") { + ctd->texture = p_value; + } else if (what == "tex_offset") { + ctd->tex_offset = p_value; + } else if (what == "material") { + ctd->material = p_value; + } else if (what == "modulate") { + ctd->modulate = p_value; + } else if (what == "region") { + ctd->region = p_value; + } else if (what == "tile_mode") { + ctd->tile_mode = p_value; + } else if (what.left(9) == "autotile") { + what = what.substr(9); + if (what == "bitmask_mode") { + ctd->autotile_bitmask_mode = p_value; + } else if (what == "icon_coordinate") { + ctd->autotile_icon_coordinate = p_value; + } else if (what == "tile_size") { + ctd->autotile_tile_size = p_value; + } else if (what == "spacing") { + ctd->autotile_spacing = p_value; + } else if (what == "bitmask_flags") { + if (p_value.is_array()) { + Array p = p_value; + Vector2i last_coord; + while (p.size() > 0) { + if (p[0].get_type() == Variant::VECTOR2) { + last_coord = p[0]; + } else if (p[0].get_type() == Variant::INT) { + ctd->autotile_bitmask_flags.insert(last_coord, p[0]); + } + p.pop_front(); + } + } + } else if (what == "occluder_map") { + Array p = p_value; + Vector2 last_coord; + while (p.size() > 0) { + if (p[0].get_type() == Variant::VECTOR2) { + last_coord = p[0]; + } else if (p[0].get_type() == Variant::OBJECT) { + ctd->autotile_occluder_map.insert(last_coord, p[0]); + } + p.pop_front(); + } + } else if (what == "navpoly_map") { + Array p = p_value; + Vector2 last_coord; + while (p.size() > 0) { + if (p[0].get_type() == Variant::VECTOR2) { + last_coord = p[0]; + } else if (p[0].get_type() == Variant::OBJECT) { + ctd->autotile_navpoly_map.insert(last_coord, p[0]); + } + p.pop_front(); + } + } else if (what == "priority_map") { + Array p = p_value; + Vector3 val; + Vector2 v; + int priority; + while (p.size() > 0) { + val = p[0]; + if (val.z > 1) { + v.x = val.x; + v.y = val.y; + priority = (int)val.z; + ctd->autotile_priority_map.insert(v, priority); + } + p.pop_front(); + } + } else if (what == "z_index_map") { + Array p = p_value; + Vector3 val; + Vector2 v; + int z_index; + while (p.size() > 0) { + val = p[0]; + if (val.z != 0) { + v.x = val.x; + v.y = val.y; + z_index = (int)val.z; + ctd->autotile_z_index_map.insert(v, z_index); + } + p.pop_front(); } - property_info.name = vformat("%s/%s", vformat("%d", E_alternative->key()), property_info.name); - tile_property_list.push_back(property_info); } - } - // Add all alternative. - for (List::Element *E_property = tile_property_list.front(); E_property; E_property = E_property->next()) { - E_property->get().name = vformat("%s/%s", vformat("%d:%d", E_tile->key().x, E_tile->key().y), E_property->get().name); - p_list->push_back(E_property->get()); - } - } -} + } else if (what == "shapes") { + Array p = p_value; + for (int i = 0; i < p.size(); i++) { + CompatibilityShapeData csd; + Dictionary d = p[i]; + for (int j = 0; j < d.size(); j++) { + String key = d.get_key_at_index(j); + if (key == "autotile_coord") { + csd.autotile_coords = d[key]; + } else if (key == "one_way") { + csd.one_way = d[key]; + } else if (key == "one_way_margin") { + csd.one_way_margin = d[key]; + } else if (key == "shape") { + csd.shape = d[key]; + } else if (key == "shape_transform") { + csd.transform = d[key]; + } + } + ctd->shapes.push_back(csd); + } -void TileSetAtlasSource::create_tile(const Vector2i p_atlas_coords, const Vector2i p_size) { - // Create a tile if it does not exists. - ERR_FAIL_COND(p_atlas_coords.x < 0 || p_atlas_coords.y < 0); - ERR_FAIL_COND(p_size.x <= 0 || p_size.y <= 0); - for (int x = 0; x < p_size.x; x++) { - for (int y = 0; y < p_size.y; y++) { - Vector2i coords = p_atlas_coords + Vector2i(x, y); - ERR_FAIL_COND_MSG(tiles.has(coords), vformat("Cannot create tile at position %s with size %s. Already a tile present at %s.", p_atlas_coords, p_size, coords)); + /* + // IGNORED FOR NOW, they seem duplicated data compared to the shapes array + } else if (what == "shape") { + // TODO + } else if (what == "shape_offset") { + // TODO + } else if (what == "shape_transform") { + // TODO + } else if (what == "shape_one_way") { + // TODO + } else if (what == "shape_one_way_margin") { + // TODO } - } - - // Create and resize the tile. - tiles.insert(p_atlas_coords, TileSetAtlasSource::TileAlternativesData()); - tiles_ids.append(p_atlas_coords); - tiles_ids.sort(); - - tiles[p_atlas_coords].size_in_atlas = p_size; - tiles[p_atlas_coords].alternatives[0] = memnew(TileData); - tiles[p_atlas_coords].alternatives[0]->set_tile_set(tile_set); - tiles[p_atlas_coords].alternatives[0]->set_allow_transform(false); - tiles[p_atlas_coords].alternatives[0]->connect("changed", callable_mp((Resource *)this, &TileSetAtlasSource::emit_changed)); - tiles[p_atlas_coords].alternatives[0]->notify_property_list_changed(); - tiles[p_atlas_coords].alternatives_ids.append(0); - - // Add all covered positions to the mapping cache - for (int x = 0; x < p_size.x; x++) { - for (int y = 0; y < p_size.y; y++) { - Vector2i coords = p_atlas_coords + Vector2i(x, y); - _coords_mapping_cache[coords] = p_atlas_coords; + // IGNORED FOR NOW, maybe useless ? + else if (what == "occluder_offset") { + // Not + } else if (what == "navigation_offset") { + // TODO } - } - - emit_signal("changed"); -} - -void TileSetAtlasSource::remove_tile(Vector2i p_atlas_coords) { - ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords))); + */ - // Remove all covered positions from the mapping cache - Size2i size = tiles[p_atlas_coords].size_in_atlas; + } else if (what == "z_index") { + ctd->z_index = p_value; - for (int x = 0; x < size.x; x++) { - for (int y = 0; y < size.y; y++) { - Vector2i coords = p_atlas_coords + Vector2i(x, y); - _coords_mapping_cache.erase(coords); + // TODO: remove the conversion from here, it's not where it should be done + compatibility_conversion(); + } else { + return false; } - } - - // Free tile data. - for (Map::Element *E_tile_data = tiles[p_atlas_coords].alternatives.front(); E_tile_data; E_tile_data = E_tile_data->next()) { - memdelete(E_tile_data->get()); - } - - // Delete the tile - tiles.erase(p_atlas_coords); - tiles_ids.erase(p_atlas_coords); - tiles_ids.sort(); - - emit_signal("changed"); -} - -bool TileSetAtlasSource::has_tile(Vector2i p_atlas_coords) const { - return tiles.has(p_atlas_coords); -} - -Vector2i TileSetAtlasSource::get_tile_at_coords(Vector2i p_atlas_coords) const { - if (!_coords_mapping_cache.has(p_atlas_coords)) { - return INVALID_ATLAS_COORDS; - } - - return _coords_mapping_cache[p_atlas_coords]; -} - -Vector2i TileSetAtlasSource::get_tile_size_in_atlas(Vector2i p_atlas_coords) const { - ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), Vector2i(-1, -1), vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords))); - - return tiles[p_atlas_coords].size_in_atlas; -} - -int TileSetAtlasSource::get_tiles_count() const { - return tiles_ids.size(); -} - -Vector2i TileSetAtlasSource::get_tile_id(int p_index) const { - ERR_FAIL_INDEX_V(p_index, tiles_ids.size(), TileSetSource::INVALID_ATLAS_COORDS); - return tiles_ids[p_index]; -} - -Rect2i TileSetAtlasSource::get_tile_texture_region(Vector2i p_atlas_coords) const { - ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), Rect2i(), vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords))); - - Vector2i size_in_atlas = tiles[p_atlas_coords].size_in_atlas; - Vector2 region_size = texture_region_size * size_in_atlas + separation * (size_in_atlas - Vector2i(1, 1)); - - Vector2 origin = margins + (p_atlas_coords * (texture_region_size + separation)); + } else { +#endif // DISABLE_DEPRECATED - return Rect2(origin, region_size); - ; -} + // This is now a new property. + if (components.size() == 2 && components[0].begins_with("occlusion_layer_") && components[0].trim_prefix("occlusion_layer_").is_valid_int()) { + // Occlusion layers. + int index = components[0].trim_prefix("occlusion_layer_").to_int(); + ERR_FAIL_COND_V(index < 0, false); + if (components[1] == "light_mask") { + ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false); + if (index >= occlusion_layers.size()) { + set_occlusion_layers_count(index + 1); + } + set_occlusion_layer_light_mask(index, p_value); + return true; + } else if (components[1] == "sdf_collision") { + ERR_FAIL_COND_V(p_value.get_type() != Variant::BOOL, false); + if (index >= occlusion_layers.size()) { + set_occlusion_layers_count(index + 1); + } + set_occlusion_layer_sdf_collision(index, p_value); + return true; + } + } else if (components.size() == 2 && components[0].begins_with("physics_layer_") && components[0].trim_prefix("physics_layer_").is_valid_int()) { + // Physics layers. + int index = components[0].trim_prefix("physics_layer_").to_int(); + ERR_FAIL_COND_V(index < 0, false); + if (components[1] == "collision_layer") { + ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false); + if (index >= physics_layers.size()) { + set_physics_layers_count(index + 1); + } + set_physics_layer_collision_layer(index, p_value); + return true; + } else if (components[1] == "collision_mask") { + ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false); + if (index >= physics_layers.size()) { + set_physics_layers_count(index + 1); + } + set_physics_layer_collision_mask(index, p_value); + return true; + } else if (components[1] == "physics_material") { + Ref physics_material = p_value; + ERR_FAIL_COND_V(!physics_material.is_valid(), false); + if (index >= physics_layers.size()) { + set_physics_layers_count(index + 1); + } + set_physics_layer_physics_material(index, physics_material); + return true; + } + } else if (components.size() >= 2 && components[0].begins_with("terrain_set_") && components[0].trim_prefix("terrain_set_").is_valid_int()) { + // Terrains. + int terrain_set_index = components[0].trim_prefix("terrain_set_").to_int(); + ERR_FAIL_COND_V(terrain_set_index < 0, false); + if (components[1] == "mode") { + ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false); + if (terrain_set_index >= terrain_sets.size()) { + set_terrain_sets_count(terrain_set_index + 1); + } + set_terrain_set_mode(terrain_set_index, TerrainMode(int(p_value))); + } else if (components[1] == "terrains_count") { + ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false); + if (terrain_set_index >= terrain_sets.size()) { + set_terrain_sets_count(terrain_set_index + 1); + } + set_terrains_count(terrain_set_index, p_value); + return true; + } else if (components.size() >= 3 && components[1].begins_with("terrain_") && components[1].trim_prefix("terrain_").is_valid_int()) { + int terrain_index = components[1].trim_prefix("terrain_").to_int(); + ERR_FAIL_COND_V(terrain_index < 0, false); + if (components[2] == "name") { + ERR_FAIL_COND_V(p_value.get_type() != Variant::STRING, false); + if (terrain_set_index >= terrain_sets.size()) { + set_terrain_sets_count(terrain_set_index + 1); + } + if (terrain_index >= terrain_sets[terrain_set_index].terrains.size()) { + set_terrains_count(terrain_set_index, terrain_index + 1); + } + set_terrain_name(terrain_set_index, terrain_index, p_value); + return true; + } else if (components[2] == "color") { + ERR_FAIL_COND_V(p_value.get_type() != Variant::COLOR, false); + if (terrain_set_index >= terrain_sets.size()) { + set_terrain_sets_count(terrain_set_index + 1); + } + if (terrain_index >= terrain_sets[terrain_set_index].terrains.size()) { + set_terrains_count(terrain_set_index, terrain_index + 1); + } + set_terrain_color(terrain_set_index, terrain_index, p_value); + return true; + } + } + } else if (components.size() == 2 && components[0].begins_with("navigation_layer_") && components[0].trim_prefix("navigation_layer_").is_valid_int()) { + // Navigation layers. + int index = components[0].trim_prefix("navigation_layer_").to_int(); + ERR_FAIL_COND_V(index < 0, false); + if (components[1] == "layers") { + ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false); + if (index >= navigation_layers.size()) { + set_navigation_layers_count(index + 1); + } + set_navigation_layer_layers(index, p_value); + return true; + } + } else if (components.size() == 2 && components[0].begins_with("custom_data_layer_") && components[0].trim_prefix("custom_data_layer_").is_valid_int()) { + // Custom data layers. + int index = components[0].trim_prefix("custom_data_layer_").to_int(); + ERR_FAIL_COND_V(index < 0, false); + if (components[1] == "name") { + ERR_FAIL_COND_V(p_value.get_type() != Variant::STRING, false); + if (index >= custom_data_layers.size()) { + set_custom_data_layers_count(index + 1); + } + set_custom_data_name(index, p_value); + return true; + } else if (components[1] == "type") { + ERR_FAIL_COND_V(p_value.get_type() != Variant::INT, false); + if (index >= custom_data_layers.size()) { + set_custom_data_layers_count(index + 1); + } + set_custom_data_type(index, Variant::Type(int(p_value))); + return true; + } + } else if (components.size() == 2 && components[0] == "sources" && components[1].is_valid_int()) { + // Create source only if it does not exists. + int source_id = components[1].to_int(); -Vector2i TileSetAtlasSource::get_tile_effective_texture_offset(Vector2i p_atlas_coords, int p_alternative_tile) const { - ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), Vector2i(), vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords))); - ERR_FAIL_COND_V_MSG(!has_alternative_tile(p_atlas_coords, p_alternative_tile), Vector2i(), vformat("TileSetAtlasSource has no alternative tile with id %d at %s.", p_alternative_tile, String(p_atlas_coords))); - ERR_FAIL_COND_V(!tile_set, Vector2i()); + if (!has_source(source_id)) { + add_source(p_value, source_id); + } + return true; + } - Vector2 margin = (get_tile_texture_region(p_atlas_coords).size - tile_set->get_tile_size()) / 2; - margin = Vector2i(MAX(0, margin.x), MAX(0, margin.y)); - Vector2i effective_texture_offset = Object::cast_to(get_tile_data(p_atlas_coords, p_alternative_tile))->get_texture_offset(); - if (ABS(effective_texture_offset.x) > margin.x || ABS(effective_texture_offset.y) > margin.y) { - effective_texture_offset.x = CLAMP(effective_texture_offset.x, -margin.x, margin.x); - effective_texture_offset.y = CLAMP(effective_texture_offset.y, -margin.y, margin.y); +#ifndef DISABLE_DEPRECATED } +#endif // DISABLE_DEPRECATED - return effective_texture_offset; + return false; } -bool TileSetAtlasSource::can_move_tile_in_atlas(Vector2i p_atlas_coords, Vector2i p_new_atlas_coords, Vector2i p_new_size) const { - ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), false, vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords))); - - Vector2i new_atlas_coords = (p_new_atlas_coords != INVALID_ATLAS_COORDS) ? p_new_atlas_coords : p_atlas_coords; - if (new_atlas_coords.x < 0 || new_atlas_coords.y < 0) { - return false; - } - - Vector2i size = (p_new_size != Vector2i(-1, -1)) ? p_new_size : tiles[p_atlas_coords].size_in_atlas; - ERR_FAIL_COND_V(size.x <= 0 || size.y <= 0, false); - - Size2i grid_size = get_atlas_grid_size(); - if (new_atlas_coords.x + size.x > grid_size.x || new_atlas_coords.y + size.y > grid_size.y) { - return false; - } +bool TileSet::_get(const StringName &p_name, Variant &r_ret) const { + Vector components = String(p_name).split("/", true, 2); - Rect2i new_rect = Rect2i(new_atlas_coords, size); - // Check if the new tile can fit in the new rect. - for (int x = new_rect.position.x; x < new_rect.get_end().x; x++) { - for (int y = new_rect.position.y; y < new_rect.get_end().y; y++) { - Vector2i coords = get_tile_at_coords(Vector2i(x, y)); - if (coords != p_atlas_coords && coords != TileSetSource::INVALID_ATLAS_COORDS) { + if (components.size() == 2 && components[0].begins_with("occlusion_layer_") && components[0].trim_prefix("occlusion_layer_").is_valid_int()) { + // Occlusion layers. + int index = components[0].trim_prefix("occlusion_layer_").to_int(); + if (index < 0 || index >= occlusion_layers.size()) { + return false; + } + if (components[1] == "light_mask") { + r_ret = get_occlusion_layer_light_mask(index); + return true; + } else if (components[1] == "sdf_collision") { + r_ret = get_occlusion_layer_sdf_collision(index); + return true; + } + } else if (components.size() == 2 && components[0].begins_with("physics_layer_") && components[0].trim_prefix("physics_layer_").is_valid_int()) { + // Physics layers. + int index = components[0].trim_prefix("physics_layer_").to_int(); + if (index < 0 || index >= physics_layers.size()) { + return false; + } + if (components[1] == "collision_layer") { + r_ret = get_physics_layer_collision_layer(index); + return true; + } else if (components[1] == "collision_mask") { + r_ret = get_physics_layer_collision_mask(index); + return true; + } else if (components[1] == "physics_material") { + r_ret = get_physics_layer_physics_material(index); + return true; + } + } else if (components.size() >= 2 && components[0].begins_with("terrain_set_") && components[0].trim_prefix("terrain_set_").is_valid_int()) { + // Terrains. + int terrain_set_index = components[0].trim_prefix("terrain_set_").to_int(); + if (terrain_set_index < 0 || terrain_set_index >= terrain_sets.size()) { + return false; + } + if (components[1] == "mode") { + r_ret = get_terrain_set_mode(terrain_set_index); + return true; + } else if (components[1] == "terrains_count") { + r_ret = get_terrains_count(terrain_set_index); + return true; + } else if (components.size() >= 3 && components[1].begins_with("terrain_") && components[1].trim_prefix("terrain_").is_valid_int()) { + int terrain_index = components[1].trim_prefix("terrain_").to_int(); + if (terrain_index < 0 || terrain_index >= terrain_sets[terrain_set_index].terrains.size()) { return false; } + if (components[2] == "name") { + r_ret = get_terrain_name(terrain_set_index, terrain_index); + return true; + } else if (components[2] == "color") { + r_ret = get_terrain_color(terrain_set_index, terrain_index); + return true; + } + } + } else if (components.size() == 2 && components[0].begins_with("navigation_layer_") && components[0].trim_prefix("navigation_layer_").is_valid_int()) { + // navigation layers. + int index = components[0].trim_prefix("navigation_layer_").to_int(); + if (index < 0 || index >= navigation_layers.size()) { + return false; + } + if (components[1] == "layers") { + r_ret = get_navigation_layer_layers(index); + return true; + } + } else if (components.size() == 2 && components[0].begins_with("custom_data_layer_") && components[0].trim_prefix("custom_data_layer_").is_valid_int()) { + // Custom data layers. + int index = components[0].trim_prefix("custom_data_layer_").to_int(); + if (index < 0 || index >= custom_data_layers.size()) { + return false; + } + if (components[1] == "name") { + r_ret = get_custom_data_name(index); + return true; + } else if (components[1] == "type") { + r_ret = get_custom_data_type(index); + return true; + } + } else if (components.size() == 2 && components[0] == "sources" && components[1].is_valid_int()) { + // Atlases data. + int source_id = components[1].to_int(); + + if (has_source(source_id)) { + r_ret = get_source(source_id); + return true; + } else { + return false; } } - return true; + return false; } -void TileSetAtlasSource::move_tile_in_atlas(Vector2i p_atlas_coords, Vector2i p_new_atlas_coords, Vector2i p_new_size) { - bool can_move = can_move_tile_in_atlas(p_atlas_coords, p_new_atlas_coords, p_new_size); - ERR_FAIL_COND_MSG(!can_move, vformat("Cannot move tile at position %s with size %s. Tile already present.", p_new_atlas_coords, p_new_size)); - - // Compute the actual new rect from arguments. - Vector2i new_atlas_coords = (p_new_atlas_coords != INVALID_ATLAS_COORDS) ? p_new_atlas_coords : p_atlas_coords; - Vector2i size = (p_new_size != Vector2i(-1, -1)) ? p_new_size : tiles[p_atlas_coords].size_in_atlas; - - if (new_atlas_coords == p_atlas_coords && size == tiles[p_atlas_coords].size_in_atlas) { - return; - } +void TileSet::_get_property_list(List *p_list) const { + PropertyInfo property_info; + // Rendering. + p_list->push_back(PropertyInfo(Variant::NIL, "Rendering", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP)); + for (int i = 0; i < occlusion_layers.size(); i++) { + p_list->push_back(PropertyInfo(Variant::INT, vformat("occlusion_layer_%d/light_mask", i), PROPERTY_HINT_LAYERS_2D_RENDER)); - // Remove all covered positions from the mapping cache. - Size2i old_size = tiles[p_atlas_coords].size_in_atlas; - for (int x = 0; x < old_size.x; x++) { - for (int y = 0; y < old_size.y; y++) { - Vector2i coords = p_atlas_coords + Vector2i(x, y); - _coords_mapping_cache.erase(coords); + // occlusion_layer_%d/sdf_collision + property_info = PropertyInfo(Variant::BOOL, vformat("occlusion_layer_%d/sdf_collision", i)); + if (occlusion_layers[i].sdf_collision == false) { + property_info.usage ^= PROPERTY_USAGE_STORAGE; } + p_list->push_back(property_info); } - // Move the tile and update its size. - if (new_atlas_coords != p_atlas_coords) { - tiles[new_atlas_coords] = tiles[p_atlas_coords]; - tiles.erase(p_atlas_coords); + // Physics. + p_list->push_back(PropertyInfo(Variant::NIL, "Physics", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP)); + for (int i = 0; i < physics_layers.size(); i++) { + p_list->push_back(PropertyInfo(Variant::INT, vformat("physics_layer_%d/collision_layer", i), PROPERTY_HINT_LAYERS_2D_PHYSICS)); - tiles_ids.erase(p_atlas_coords); - tiles_ids.append(new_atlas_coords); - tiles_ids.sort(); - } - tiles[new_atlas_coords].size_in_atlas = size; + // physics_layer_%d/collision_mask + property_info = PropertyInfo(Variant::INT, vformat("physics_layer_%d/collision_mask", i), PROPERTY_HINT_LAYERS_2D_PHYSICS); + if (physics_layers[i].collision_mask == 1) { + property_info.usage ^= PROPERTY_USAGE_STORAGE; + } + p_list->push_back(property_info); - // Add all covered positions to the mapping cache again. - for (int x = 0; x < size.x; x++) { - for (int y = 0; y < size.y; y++) { - Vector2i coords = new_atlas_coords + Vector2i(x, y); - _coords_mapping_cache[coords] = new_atlas_coords; + // physics_layer_%d/physics_material + property_info = PropertyInfo(Variant::OBJECT, vformat("physics_layer_%d/physics_material", i), PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"); + if (!physics_layers[i].physics_material.is_valid()) { + property_info.usage ^= PROPERTY_USAGE_STORAGE; } + p_list->push_back(property_info); } - emit_signal("changed"); -} - -bool TileSetAtlasSource::has_tiles_outside_texture() { - Vector2i grid_size = get_atlas_grid_size(); - Vector to_remove; - - for (Map::Element *E = tiles.front(); E; E = E->next()) { - if (E->key().x >= grid_size.x || E->key().y >= grid_size.y) { - return true; + // Terrains. + p_list->push_back(PropertyInfo(Variant::NIL, "Terrains", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP)); + for (int terrain_set_index = 0; terrain_set_index < terrain_sets.size(); terrain_set_index++) { + p_list->push_back(PropertyInfo(Variant::INT, vformat("terrain_set_%d/mode", terrain_set_index), PROPERTY_HINT_ENUM, "Match corners and sides,Match corners,Match sides")); + p_list->push_back(PropertyInfo(Variant::INT, vformat("terrain_set_%d/terrains_count", terrain_set_index), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR)); + for (int terrain_index = 0; terrain_index < terrain_sets[terrain_set_index].terrains.size(); terrain_index++) { + p_list->push_back(PropertyInfo(Variant::STRING, vformat("terrain_set_%d/terrain_%d/name", terrain_set_index, terrain_index))); + p_list->push_back(PropertyInfo(Variant::COLOR, vformat("terrain_set_%d/terrain_%d/color", terrain_set_index, terrain_index))); } } - return false; -} - -void TileSetAtlasSource::clear_tiles_outside_texture() { - Vector2i grid_size = get_atlas_grid_size(); - Vector to_remove; - - for (Map::Element *E = tiles.front(); E; E = E->next()) { - if (E->key().x >= grid_size.x || E->key().y >= grid_size.y) { - to_remove.append(E->key()); - } + // Navigation. + p_list->push_back(PropertyInfo(Variant::NIL, "Navigation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP)); + for (int i = 0; i < navigation_layers.size(); i++) { + p_list->push_back(PropertyInfo(Variant::INT, vformat("navigation_layer_%d/layers", i), PROPERTY_HINT_LAYERS_2D_NAVIGATION)); + } + + // Custom data. + String argt = "Any"; + for (int i = 1; i < Variant::VARIANT_MAX; i++) { + argt += "," + Variant::get_type_name(Variant::Type(i)); + } + p_list->push_back(PropertyInfo(Variant::NIL, "Custom data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP)); + for (int i = 0; i < custom_data_layers.size(); i++) { + p_list->push_back(PropertyInfo(Variant::STRING, vformat("custom_data_layer_%d/name", i))); + p_list->push_back(PropertyInfo(Variant::INT, vformat("custom_data_layer_%d/type", i), PROPERTY_HINT_ENUM, argt)); } - for (int i = 0; i < to_remove.size(); i++) { - remove_tile(to_remove[i]); + // Sources. + // Note: sources have to be listed in at the end as some TileData rely on the TileSet properties being initialized first. + for (Map>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) { + p_list->push_back(PropertyInfo(Variant::INT, vformat("sources/%d", E_source->key()), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); } } -int TileSetAtlasSource::create_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_id_override) { - ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), -1, vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords))); - ERR_FAIL_COND_V_MSG(p_alternative_id_override >= 0 && tiles[p_atlas_coords].alternatives.has(p_alternative_id_override), -1, vformat("Cannot create alternative tile. Another alternative exists with id %d.", p_alternative_id_override)); +void TileSet::_bind_methods() { + // Sources management. + ClassDB::bind_method(D_METHOD("get_next_source_id"), &TileSet::get_next_source_id); + ClassDB::bind_method(D_METHOD("add_source", "atlas_source_id_override"), &TileSet::add_source, DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("remove_source", "source_id"), &TileSet::remove_source); + ClassDB::bind_method(D_METHOD("set_source_id", "source_id"), &TileSet::set_source_id); + ClassDB::bind_method(D_METHOD("get_source_count"), &TileSet::get_source_count); + ClassDB::bind_method(D_METHOD("get_source_id", "index"), &TileSet::get_source_id); + ClassDB::bind_method(D_METHOD("has_source", "index"), &TileSet::has_source); + ClassDB::bind_method(D_METHOD("get_source", "index"), &TileSet::get_source); - int new_alternative_id = p_alternative_id_override >= 0 ? p_alternative_id_override : tiles[p_atlas_coords].next_alternative_id; + // Shape and layout. + ClassDB::bind_method(D_METHOD("set_tile_shape", "shape"), &TileSet::set_tile_shape); + ClassDB::bind_method(D_METHOD("get_tile_shape"), &TileSet::get_tile_shape); + ClassDB::bind_method(D_METHOD("set_tile_layout", "layout"), &TileSet::set_tile_layout); + ClassDB::bind_method(D_METHOD("get_tile_layout"), &TileSet::get_tile_layout); + ClassDB::bind_method(D_METHOD("set_tile_offset_axis", "alignment"), &TileSet::set_tile_offset_axis); + ClassDB::bind_method(D_METHOD("get_tile_offset_axis"), &TileSet::get_tile_offset_axis); + ClassDB::bind_method(D_METHOD("set_tile_size", "size"), &TileSet::set_tile_size); + ClassDB::bind_method(D_METHOD("get_tile_size"), &TileSet::get_tile_size); - tiles[p_atlas_coords].alternatives[new_alternative_id] = memnew(TileData); - tiles[p_atlas_coords].alternatives[new_alternative_id]->set_tile_set(tile_set); - tiles[p_atlas_coords].alternatives[new_alternative_id]->set_allow_transform(true); - tiles[p_atlas_coords].alternatives[new_alternative_id]->notify_property_list_changed(); - tiles[p_atlas_coords].alternatives_ids.append(new_alternative_id); - tiles[p_atlas_coords].alternatives_ids.sort(); - _compute_next_alternative_id(p_atlas_coords); + ADD_PROPERTY(PropertyInfo(Variant::INT, "tile_shape", PROPERTY_HINT_ENUM, "Square,Isometric,Half-Offset Square,Hexagon"), "set_tile_shape", "get_tile_shape"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "tile_layout", PROPERTY_HINT_ENUM, "Stacked,Stacked Offset,Stairs Right,Stairs Down,Diamond Right,Diamond Down"), "set_tile_layout", "get_tile_layout"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "tile_offset_axis", PROPERTY_HINT_ENUM, "Horizontal Offset,Vertical Offset"), "set_tile_offset_axis", "get_tile_offset_axis"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "tile_size"), "set_tile_size", "get_tile_size"); - emit_signal("changed"); + // Rendering. + ClassDB::bind_method(D_METHOD("set_uv_clipping", "uv_clipping"), &TileSet::set_uv_clipping); + ClassDB::bind_method(D_METHOD("is_uv_clipping"), &TileSet::is_uv_clipping); + ClassDB::bind_method(D_METHOD("set_y_sorting", "y_sorting"), &TileSet::set_y_sorting); + ClassDB::bind_method(D_METHOD("is_y_sorting"), &TileSet::is_y_sorting); - return new_alternative_id; -} + ClassDB::bind_method(D_METHOD("set_occlusion_layers_count", "occlusion_layers_count"), &TileSet::set_occlusion_layers_count); + ClassDB::bind_method(D_METHOD("get_occlusion_layers_count"), &TileSet::get_occlusion_layers_count); + ClassDB::bind_method(D_METHOD("set_occlusion_layer_light_mask", "layer_index", "light_mask"), &TileSet::set_occlusion_layer_light_mask); + ClassDB::bind_method(D_METHOD("get_occlusion_layer_light_mask"), &TileSet::get_occlusion_layer_light_mask); + ClassDB::bind_method(D_METHOD("set_occlusion_layer_sdf_collision", "layer_index", "sdf_collision"), &TileSet::set_occlusion_layer_sdf_collision); + ClassDB::bind_method(D_METHOD("get_occlusion_layer_sdf_collision"), &TileSet::get_occlusion_layer_sdf_collision); -void TileSetAtlasSource::remove_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_tile) { - ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords))); - ERR_FAIL_COND_MSG(!tiles[p_atlas_coords].alternatives.has(p_alternative_tile), vformat("TileSetAtlasSource has no alternative with id %d for tile coords %s.", p_alternative_tile, String(p_atlas_coords))); - ERR_FAIL_COND_MSG(p_alternative_tile == 0, "Cannot remove the alternative with id 0, the base tile alternative cannot be removed."); + // Physics + ClassDB::bind_method(D_METHOD("set_physics_layers_count", "physics_layers_count"), &TileSet::set_physics_layers_count); + ClassDB::bind_method(D_METHOD("get_physics_layers_count"), &TileSet::get_physics_layers_count); + ClassDB::bind_method(D_METHOD("set_physics_layer_collision_layer", "layer_index", "layer"), &TileSet::set_physics_layer_collision_layer); + ClassDB::bind_method(D_METHOD("get_physics_layer_collision_layer", "layer_index"), &TileSet::get_physics_layer_collision_layer); + ClassDB::bind_method(D_METHOD("set_physics_layer_collision_mask", "layer_index", "mask"), &TileSet::set_physics_layer_collision_mask); + ClassDB::bind_method(D_METHOD("get_physics_layer_collision_mask", "layer_index"), &TileSet::get_physics_layer_collision_mask); + ClassDB::bind_method(D_METHOD("set_physics_layer_physics_material", "layer_index", "physics_material"), &TileSet::set_physics_layer_physics_material); + ClassDB::bind_method(D_METHOD("get_physics_layer_physics_material", "layer_index"), &TileSet::get_physics_layer_physics_material); - memdelete(tiles[p_atlas_coords].alternatives[p_alternative_tile]); - tiles[p_atlas_coords].alternatives.erase(p_alternative_tile); - tiles[p_atlas_coords].alternatives_ids.erase(p_alternative_tile); - tiles[p_atlas_coords].alternatives_ids.sort(); + // Terrains + ClassDB::bind_method(D_METHOD("set_terrain_sets_count", "terrain_sets_count"), &TileSet::set_terrain_sets_count); + ClassDB::bind_method(D_METHOD("get_terrain_sets_count"), &TileSet::get_terrain_sets_count); + ClassDB::bind_method(D_METHOD("set_terrain_set_mode", "terrain_set", "mode"), &TileSet::set_terrain_set_mode); + ClassDB::bind_method(D_METHOD("get_terrain_set_mode", "terrain_set"), &TileSet::get_terrain_set_mode); - emit_signal("changed"); -} + ClassDB::bind_method(D_METHOD("set_terrains_count", "terrain_set", "terrains_count"), &TileSet::set_terrains_count); + ClassDB::bind_method(D_METHOD("get_terrains_count", "terrain_set"), &TileSet::get_terrains_count); + ClassDB::bind_method(D_METHOD("set_terrain_name", "terrain_set", "terrain_index", "name"), &TileSet::set_terrain_name); + ClassDB::bind_method(D_METHOD("get_terrain_name", "terrain_set", "terrain_index"), &TileSet::get_terrain_name); + ClassDB::bind_method(D_METHOD("set_terrain_color", "terrain_set", "terrain_index", "color"), &TileSet::set_terrain_color); + ClassDB::bind_method(D_METHOD("get_terrain_color", "terrain_set", "terrain_index"), &TileSet::get_terrain_color); -void TileSetAtlasSource::set_alternative_tile_id(const Vector2i p_atlas_coords, int p_alternative_tile, int p_new_id) { - ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords))); - ERR_FAIL_COND_MSG(!tiles[p_atlas_coords].alternatives.has(p_alternative_tile), vformat("TileSetAtlasSource has no alternative with id %d for tile coords %s.", p_alternative_tile, String(p_atlas_coords))); - ERR_FAIL_COND_MSG(p_alternative_tile == 0, "Cannot change the alternative with id 0, the base tile alternative cannot be modified."); + // Navigation + ClassDB::bind_method(D_METHOD("set_navigation_layers_count", "navigation_layers_count"), &TileSet::set_navigation_layers_count); + ClassDB::bind_method(D_METHOD("get_navigation_layers_count"), &TileSet::get_navigation_layers_count); + ClassDB::bind_method(D_METHOD("set_navigation_layer_layers", "layer_index", "layers"), &TileSet::set_navigation_layer_layers); + ClassDB::bind_method(D_METHOD("get_navigation_layer_layers", "layer_index"), &TileSet::get_navigation_layer_layers); - ERR_FAIL_COND_MSG(tiles[p_atlas_coords].alternatives.has(p_new_id), vformat("TileSetAtlasSource has already an alternative with id %d at %s.", p_new_id, String(p_atlas_coords))); + // Custom data + ClassDB::bind_method(D_METHOD("set_custom_data_layers_count", "custom_data_layers_count"), &TileSet::set_custom_data_layers_count); + ClassDB::bind_method(D_METHOD("get_custom_data_layers_count"), &TileSet::get_custom_data_layers_count); - tiles[p_atlas_coords].alternatives[p_new_id] = tiles[p_atlas_coords].alternatives[p_alternative_tile]; - tiles[p_atlas_coords].alternatives_ids.append(p_new_id); + ADD_GROUP("Rendering", ""); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "uv_clipping"), "set_uv_clipping", "is_uv_clipping"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "y_sorting"), "set_y_sorting", "is_y_sorting"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "occlusion_layers_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_occlusion_layers_count", "get_occlusion_layers_count"); - tiles[p_atlas_coords].alternatives.erase(p_alternative_tile); - tiles[p_atlas_coords].alternatives_ids.erase(p_alternative_tile); - tiles[p_atlas_coords].alternatives_ids.sort(); + ADD_GROUP("Physics", ""); + ADD_PROPERTY(PropertyInfo(Variant::INT, "physics_layers_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_physics_layers_count", "get_physics_layers_count"); - emit_signal("changed"); -} + ADD_GROUP("Terrains", ""); + ADD_PROPERTY(PropertyInfo(Variant::INT, "terrains_sets_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_terrain_sets_count", "get_terrain_sets_count"); -bool TileSetAtlasSource::has_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_tile) const { - ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), false, vformat("The TileSetAtlasSource atlas has no tile at %s.", String(p_atlas_coords))); - return tiles[p_atlas_coords].alternatives.has(p_alternative_tile); -} + ADD_GROUP("Navigation", ""); + ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_layers_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_navigation_layers_count", "get_navigation_layers_count"); -int TileSetAtlasSource::get_next_alternative_tile_id(const Vector2i p_atlas_coords) const { - ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), -1, vformat("The TileSetAtlasSource atlas has no tile at %s.", String(p_atlas_coords))); - return tiles[p_atlas_coords].next_alternative_id; -} + ADD_GROUP("Custom data", ""); + ADD_PROPERTY(PropertyInfo(Variant::INT, "custom_data_layers_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_custom_data_layers_count", "get_custom_data_layers_count"); -int TileSetAtlasSource::get_alternative_tiles_count(const Vector2i p_atlas_coords) const { - ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), -1, vformat("The TileSetAtlasSource atlas has no tile at %s.", String(p_atlas_coords))); - return tiles[p_atlas_coords].alternatives_ids.size(); -} + // -- Enum binding -- + BIND_ENUM_CONSTANT(TILE_SHAPE_SQUARE); + BIND_ENUM_CONSTANT(TILE_SHAPE_ISOMETRIC); + BIND_ENUM_CONSTANT(TILE_SHAPE_HALF_OFFSET_SQUARE); + BIND_ENUM_CONSTANT(TILE_SHAPE_HEXAGON); -int TileSetAtlasSource::get_alternative_tile_id(const Vector2i p_atlas_coords, int p_index) const { - ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), -1, vformat("The TileSetAtlasSource atlas has no tile at %s.", String(p_atlas_coords))); - ERR_FAIL_INDEX_V(p_index, tiles[p_atlas_coords].alternatives_ids.size(), -1); + BIND_ENUM_CONSTANT(TILE_LAYOUT_STACKED); + BIND_ENUM_CONSTANT(TILE_LAYOUT_STACKED_OFFSET); + BIND_ENUM_CONSTANT(TILE_LAYOUT_STAIRS_RIGHT); + BIND_ENUM_CONSTANT(TILE_LAYOUT_STAIRS_DOWN); + BIND_ENUM_CONSTANT(TILE_LAYOUT_DIAMOND_RIGHT); + BIND_ENUM_CONSTANT(TILE_LAYOUT_DIAMOND_DOWN); - return tiles[p_atlas_coords].alternatives_ids[p_index]; -} + BIND_ENUM_CONSTANT(TILE_OFFSET_AXIS_HORIZONTAL); + BIND_ENUM_CONSTANT(TILE_OFFSET_AXIS_VERTICAL); -Object *TileSetAtlasSource::get_tile_data(const Vector2i p_atlas_coords, int p_alternative_tile) const { - ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), nullptr, vformat("The TileSetAtlasSource atlas has no tile at %s.", String(p_atlas_coords))); - ERR_FAIL_COND_V_MSG(!tiles[p_atlas_coords].alternatives.has(p_alternative_tile), nullptr, vformat("TileSetAtlasSource has no alternative with id %d for tile coords %s.", p_alternative_tile, String(p_atlas_coords))); + BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_RIGHT_SIDE); + BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_RIGHT_CORNER); + BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE); + BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER); + BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_BOTTOM_SIDE); + BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_BOTTOM_CORNER); + BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE); + BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER); + BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_LEFT_SIDE); + BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_LEFT_CORNER); + BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE); + BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER); + BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_TOP_SIDE); + BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_TOP_CORNER); + BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE); + BIND_ENUM_CONSTANT(TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER); - return tiles[p_atlas_coords].alternatives[p_alternative_tile]; + BIND_ENUM_CONSTANT(TERRAIN_MODE_MATCH_CORNERS_AND_SIDES); + BIND_ENUM_CONSTANT(TERRAIN_MODE_MATCH_CORNERS); + BIND_ENUM_CONSTANT(TERRAIN_MODE_MATCH_SIDES); } -void TileSetAtlasSource::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_texture", "texture"), &TileSetAtlasSource::set_texture); - ClassDB::bind_method(D_METHOD("get_texture"), &TileSetAtlasSource::get_texture); - ClassDB::bind_method(D_METHOD("set_margins", "margins"), &TileSetAtlasSource::set_margins); - ClassDB::bind_method(D_METHOD("get_margins"), &TileSetAtlasSource::get_margins); - ClassDB::bind_method(D_METHOD("set_separation", "separation"), &TileSetAtlasSource::set_separation); - ClassDB::bind_method(D_METHOD("get_separation"), &TileSetAtlasSource::get_separation); - ClassDB::bind_method(D_METHOD("set_texture_region_size", "texture_region_size"), &TileSetAtlasSource::set_texture_region_size); - ClassDB::bind_method(D_METHOD("get_texture_region_size"), &TileSetAtlasSource::get_texture_region_size); +TileSet::TileSet() { + // Instantiate the tile meshes. + tile_lines_mesh.instantiate(); + tile_filled_mesh.instantiate(); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D", PROPERTY_USAGE_NOEDITOR), "set_texture", "get_texture"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "margins", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_margins", "get_margins"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "separation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_separation", "get_separation"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "tile_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_texture_region_size", "get_texture_region_size"); + // Instanciate and list all plugins. + tile_set_plugins_vector.append(memnew(TileSetPluginAtlasRendering)); + tile_set_plugins_vector.append(memnew(TileSetPluginAtlasPhysics)); + tile_set_plugins_vector.append(memnew(TileSetPluginAtlasNavigation)); + tile_set_plugins_vector.append(memnew(TileSetPluginScenesCollections)); +} - // Base tiles - ClassDB::bind_method(D_METHOD("create_tile", "atlas_coords", "size"), &TileSetAtlasSource::create_tile, DEFVAL(Vector2i(1, 1))); - ClassDB::bind_method(D_METHOD("remove_tile", "atlas_coords"), &TileSetAtlasSource::remove_tile); // Remove a tile. If p_tile_key.alternative_tile if different from 0, remove the alternative - ClassDB::bind_method(D_METHOD("has_tile", "atlas_coords"), &TileSetAtlasSource::has_tile); - ClassDB::bind_method(D_METHOD("can_move_tile_in_atlas", "atlas_coords", "new_atlas_coords", "new_size"), &TileSetAtlasSource::can_move_tile_in_atlas, DEFVAL(INVALID_ATLAS_COORDS), DEFVAL(Vector2i(-1, -1))); - ClassDB::bind_method(D_METHOD("move_tile_in_atlas", "atlas_coords", "new_atlas_coords", "new_size"), &TileSetAtlasSource::move_tile_in_atlas, DEFVAL(INVALID_ATLAS_COORDS), DEFVAL(Vector2i(-1, -1))); - ClassDB::bind_method(D_METHOD("get_tile_size_in_atlas", "atlas_coords"), &TileSetAtlasSource::get_tile_size_in_atlas); +TileSet::~TileSet() { +#ifndef DISABLE_DEPRECATED + for (Map::Element *E = compatibility_data.front(); E; E = E->next()) { + memdelete(E->get()); + } +#endif // DISABLE_DEPRECATED + while (!source_ids.is_empty()) { + remove_source(source_ids[0]); + } + for (int i = 0; i < tile_set_plugins_vector.size(); i++) { + memdelete(tile_set_plugins_vector[i]); + } +} - ClassDB::bind_method(D_METHOD("get_tiles_count"), &TileSetAtlasSource::get_tiles_count); - ClassDB::bind_method(D_METHOD("get_tile_id", "index"), &TileSetAtlasSource::get_tile_id); +/////////////////////////////// TileSetSource ////////////////////////////////////// - ClassDB::bind_method(D_METHOD("get_tile_at_coords", "atlas_coords"), &TileSetAtlasSource::get_tile_at_coords); +void TileSetSource::set_tile_set(const TileSet *p_tile_set) { + tile_set = p_tile_set; +} - // Alternative tiles - ClassDB::bind_method(D_METHOD("create_alternative_tile", "atlas_coords", "alternative_id_override"), &TileSetAtlasSource::create_alternative_tile, DEFVAL(-1)); - ClassDB::bind_method(D_METHOD("remove_alternative_tile", "atlas_coords", "alternative_tile"), &TileSetAtlasSource::remove_alternative_tile); - ClassDB::bind_method(D_METHOD("set_alternative_tile_id", "atlas_coords", "alternative_tile", "new_id"), &TileSetAtlasSource::set_alternative_tile_id); - ClassDB::bind_method(D_METHOD("has_alternative_tile", "atlas_coords", "alternative_tile"), &TileSetAtlasSource::has_alternative_tile); - ClassDB::bind_method(D_METHOD("get_next_alternative_tile_id", "atlas_coords"), &TileSetAtlasSource::get_next_alternative_tile_id); +/////////////////////////////// TileSetAtlasSource ////////////////////////////////////// - ClassDB::bind_method(D_METHOD("get_alternative_tiles_count", "atlas_coords"), &TileSetAtlasSource::get_alternative_tiles_count); - ClassDB::bind_method(D_METHOD("get_alternative_tile_id", "atlas_coords", "index"), &TileSetAtlasSource::get_alternative_tile_id); +void TileSetAtlasSource::set_tile_set(const TileSet *p_tile_set) { + tile_set = p_tile_set; - ClassDB::bind_method(D_METHOD("get_tile_data", "atlas_coords", "index"), &TileSetAtlasSource::get_tile_data); + // Set the TileSet on all TileData. + for (Map::Element *E_tile = tiles.front(); E_tile; E_tile = E_tile->next()) { + for (Map::Element *E_alternative = E_tile->get().alternatives.front(); E_alternative; E_alternative = E_alternative->next()) { + E_alternative->get()->set_tile_set(tile_set); + } + } +} - // Helpers. - ClassDB::bind_method(D_METHOD("get_atlas_grid_size"), &TileSetAtlasSource::get_atlas_grid_size); - ClassDB::bind_method(D_METHOD("has_tiles_outside_texture"), &TileSetAtlasSource::has_tiles_outside_texture); - ClassDB::bind_method(D_METHOD("clear_tiles_outside_texture"), &TileSetAtlasSource::clear_tiles_outside_texture); - ClassDB::bind_method(D_METHOD("get_tile_texture_region", "atlas_coords"), &TileSetAtlasSource::get_tile_texture_region); +void TileSetAtlasSource::notify_tile_data_properties_should_change() { + // Set the TileSet on all TileData. + for (Map::Element *E_tile = tiles.front(); E_tile; E_tile = E_tile->next()) { + for (Map::Element *E_alternative = E_tile->get().alternatives.front(); E_alternative; E_alternative = E_alternative->next()) { + E_alternative->get()->notify_tile_data_properties_should_change(); + } + } } -TileSetAtlasSource::~TileSetAtlasSource() { - // Free everything needed. - for (Map::Element *E_alternatives = tiles.front(); E_alternatives; E_alternatives = E_alternatives->next()) { - for (Map::Element *E_tile_data = E_alternatives->get().alternatives.front(); E_tile_data; E_tile_data = E_tile_data->next()) { - memdelete(E_tile_data->get()); +void TileSetAtlasSource::reset_state() { + // Reset all TileData. + for (Map::Element *E_tile = tiles.front(); E_tile; E_tile = E_tile->next()) { + for (Map::Element *E_alternative = E_tile->get().alternatives.front(); E_alternative; E_alternative = E_alternative->next()) { + E_alternative->get()->reset_state(); } } } -TileData *TileSetAtlasSource::_get_atlas_tile_data(Vector2i p_atlas_coords, int p_alternative_tile) { - ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), nullptr, vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords))); - ERR_FAIL_COND_V_MSG(!tiles[p_atlas_coords].alternatives.has(p_alternative_tile), nullptr, vformat("TileSetAtlasSource has no alternative with id %d for tile coords %s.", p_alternative_tile, String(p_atlas_coords))); +void TileSetAtlasSource::set_texture(Ref p_texture) { + texture = p_texture; - return tiles[p_atlas_coords].alternatives[p_alternative_tile]; + emit_changed(); } -const TileData *TileSetAtlasSource::_get_atlas_tile_data(Vector2i p_atlas_coords, int p_alternative_tile) const { - ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), nullptr, vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords))); - ERR_FAIL_COND_V_MSG(!tiles[p_atlas_coords].alternatives.has(p_alternative_tile), nullptr, vformat("TileSetAtlasSource has no alternative with id %d for tile coords %s.", p_alternative_tile, String(p_atlas_coords))); - - return tiles[p_atlas_coords].alternatives[p_alternative_tile]; +Ref TileSetAtlasSource::get_texture() const { + return texture; } -void TileSetAtlasSource::_compute_next_alternative_id(const Vector2i p_atlas_coords) { - ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords))); +void TileSetAtlasSource::set_margins(Vector2i p_margins) { + if (p_margins.x < 0 || p_margins.y < 0) { + WARN_PRINT("Atlas source margins should be positive."); + margins = Vector2i(MAX(0, p_margins.x), MAX(0, p_margins.y)); + } else { + margins = p_margins; + } - while (tiles[p_atlas_coords].alternatives.has(tiles[p_atlas_coords].next_alternative_id)) { - tiles[p_atlas_coords].next_alternative_id = (tiles[p_atlas_coords].next_alternative_id % 1073741823) + 1; // 2 ** 30 - }; + emit_changed(); +} +Vector2i TileSetAtlasSource::get_margins() const { + return margins; } -/////////////////////////////// TileSetScenesCollectionSource ////////////////////////////////////// +void TileSetAtlasSource::set_separation(Vector2i p_separation) { + if (p_separation.x < 0 || p_separation.y < 0) { + WARN_PRINT("Atlas source separation should be positive."); + separation = Vector2i(MAX(0, p_separation.x), MAX(0, p_separation.y)); + } else { + separation = p_separation; + } -void TileSetScenesCollectionSource::_compute_next_alternative_id() { - while (scenes.has(next_scene_id)) { - next_scene_id = (next_scene_id % 1073741823) + 1; // 2 ** 30 - }; + emit_changed(); } - -int TileSetScenesCollectionSource::get_tiles_count() const { - return 1; +Vector2i TileSetAtlasSource::get_separation() const { + return separation; } -Vector2i TileSetScenesCollectionSource::get_tile_id(int p_tile_index) const { - ERR_FAIL_COND_V(p_tile_index != 0, TileSetSource::INVALID_ATLAS_COORDS); - return Vector2i(); +void TileSetAtlasSource::set_texture_region_size(Vector2i p_tile_size) { + if (p_tile_size.x <= 0 || p_tile_size.y <= 0) { + WARN_PRINT("Atlas source tile_size should be strictly positive."); + texture_region_size = Vector2i(MAX(1, p_tile_size.x), MAX(1, p_tile_size.y)); + } else { + texture_region_size = p_tile_size; + } + + emit_changed(); +} +Vector2i TileSetAtlasSource::get_texture_region_size() const { + return texture_region_size; } -bool TileSetScenesCollectionSource::has_tile(Vector2i p_atlas_coords) const { - return p_atlas_coords == Vector2i(); +Vector2i TileSetAtlasSource::get_atlas_grid_size() const { + Ref texture = get_texture(); + if (!texture.is_valid()) { + return Vector2i(); + } + + ERR_FAIL_COND_V(texture_region_size.x <= 0 || texture_region_size.y <= 0, Vector2i()); + + Size2i valid_area = texture->get_size() - margins; + + // Compute the number of valid tiles in the tiles atlas + Size2i grid_size = Size2i(); + if (valid_area.x >= texture_region_size.x && valid_area.y >= texture_region_size.y) { + valid_area -= texture_region_size; + grid_size = Size2i(1, 1) + valid_area / (texture_region_size + separation); + } + return grid_size; } -int TileSetScenesCollectionSource::get_alternative_tiles_count(const Vector2i p_atlas_coords) const { - return scenes_ids.size(); +bool TileSetAtlasSource::_set(const StringName &p_name, const Variant &p_value) { + Vector components = String(p_name).split("/", true, 2); + + // Compute the vector2i if we have coordinates. + Vector coords_split = components[0].split(":"); + Vector2i coords = TileSetSource::INVALID_ATLAS_COORDS; + if (coords_split.size() == 2 && coords_split[0].is_valid_int() && coords_split[1].is_valid_int()) { + coords = Vector2i(coords_split[0].to_int(), coords_split[1].to_int()); + } + + // Properties. + if (coords != TileSetSource::INVALID_ATLAS_COORDS) { + // Create the tile if needed. + if (!has_tile(coords)) { + create_tile(coords); + } + if (components.size() >= 2) { + // Properties. + if (components[1] == "size_in_atlas") { + move_tile_in_atlas(coords, coords, p_value); + } else if (components[1] == "next_alternative_id") { + tiles[coords].next_alternative_id = p_value; + } else if (components[1].is_valid_int()) { + int alternative_id = components[1].to_int(); + if (alternative_id != TileSetSource::INVALID_TILE_ALTERNATIVE) { + // Create the alternative if needed ? + if (!has_alternative_tile(coords, alternative_id)) { + create_alternative_tile(coords, alternative_id); + } + if (!tiles[coords].alternatives.has(alternative_id)) { + tiles[coords].alternatives[alternative_id] = memnew(TileData); + tiles[coords].alternatives[alternative_id]->set_tile_set(tile_set); + tiles[coords].alternatives[alternative_id]->set_allow_transform(alternative_id > 0); + tiles[coords].alternatives_ids.append(alternative_id); + } + if (components.size() >= 3) { + bool valid; + tiles[coords].alternatives[alternative_id]->set(components[2], p_value, &valid); + return valid; + } else { + // Only create the alternative if it did not exist yet. + return true; + } + } + } + } + } + + return false; } -int TileSetScenesCollectionSource::get_alternative_tile_id(const Vector2i p_atlas_coords, int p_index) const { - ERR_FAIL_COND_V(p_atlas_coords != Vector2i(), TileSetSource::INVALID_TILE_ALTERNATIVE); - ERR_FAIL_INDEX_V(p_index, scenes_ids.size(), TileSetSource::INVALID_TILE_ALTERNATIVE); +bool TileSetAtlasSource::_get(const StringName &p_name, Variant &r_ret) const { + Vector components = String(p_name).split("/", true, 2); - return scenes_ids[p_index]; + // Properties. + Vector coords_split = components[0].split(":"); + if (coords_split.size() == 2 && coords_split[0].is_valid_int() && coords_split[1].is_valid_int()) { + Vector2i coords = Vector2i(coords_split[0].to_int(), coords_split[1].to_int()); + if (tiles.has(coords)) { + if (components.size() >= 2) { + // Properties. + if (components[1] == "size_in_atlas") { + r_ret = tiles[coords].size_in_atlas; + return true; + } else if (components[1] == "next_alternative_id") { + r_ret = tiles[coords].next_alternative_id; + return true; + } else if (components[1].is_valid_int()) { + int alternative_id = components[1].to_int(); + if (alternative_id != TileSetSource::INVALID_TILE_ALTERNATIVE && tiles[coords].alternatives.has(alternative_id)) { + if (components.size() >= 3) { + bool valid; + r_ret = tiles[coords].alternatives[alternative_id]->get(components[2], &valid); + return valid; + } else { + // Only to notify the tile alternative exists. + r_ret = alternative_id; + return true; + } + } + } + } + } + } + + return false; } -bool TileSetScenesCollectionSource::has_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_tile) const { - ERR_FAIL_COND_V(p_atlas_coords != Vector2i(), false); - return scenes.has(p_alternative_tile); +void TileSetAtlasSource::_get_property_list(List *p_list) const { + // Atlases data. + PropertyInfo property_info; + for (Map::Element *E_tile = tiles.front(); E_tile; E_tile = E_tile->next()) { + List tile_property_list; + + // size_in_atlas + property_info = PropertyInfo(Variant::VECTOR2I, "size_in_atlas", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR); + if (E_tile->get().size_in_atlas == Vector2i(1, 1)) { + property_info.usage ^= PROPERTY_USAGE_STORAGE; + } + tile_property_list.push_back(property_info); + + // next_alternative_id + property_info = PropertyInfo(Variant::INT, "next_alternative_id", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR); + if (E_tile->get().next_alternative_id == 1) { + property_info.usage ^= PROPERTY_USAGE_STORAGE; + } + tile_property_list.push_back(property_info); + + for (Map::Element *E_alternative = E_tile->get().alternatives.front(); E_alternative; E_alternative = E_alternative->next()) { + // Add a dummy property to show the alternative exists. + tile_property_list.push_back(PropertyInfo(Variant::INT, vformat("%d", E_alternative->key()), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); + + // Get the alternative tile's properties and append them to the list of properties. + List alternative_property_list; + E_alternative->get()->get_property_list(&alternative_property_list); + for (List::Element *E_property = alternative_property_list.front(); E_property; E_property = E_property->next()) { + property_info = E_property->get(); + bool valid; + Variant default_value = ClassDB::class_get_default_property_value("TileData", property_info.name, &valid); + Variant value = E_alternative->get()->get(property_info.name); + if (valid && value == default_value) { + property_info.usage ^= PROPERTY_USAGE_STORAGE; + } + property_info.name = vformat("%s/%s", vformat("%d", E_alternative->key()), property_info.name); + tile_property_list.push_back(property_info); + } + } + + // Add all alternative. + for (List::Element *E_property = tile_property_list.front(); E_property; E_property = E_property->next()) { + E_property->get().name = vformat("%s/%s", vformat("%d:%d", E_tile->key().x, E_tile->key().y), E_property->get().name); + p_list->push_back(E_property->get()); + } + } } -int TileSetScenesCollectionSource::create_scene_tile(Ref p_packed_scene, int p_id_override) { - ERR_FAIL_COND_V_MSG(p_id_override >= 0 && scenes.has(p_id_override), -1, vformat("Cannot create scene tile. Another scene tile exists with id %d.", p_id_override)); +void TileSetAtlasSource::create_tile(const Vector2i p_atlas_coords, const Vector2i p_size) { + // Create a tile if it does not exists. + ERR_FAIL_COND(p_atlas_coords.x < 0 || p_atlas_coords.y < 0); + ERR_FAIL_COND(p_size.x <= 0 || p_size.y <= 0); + for (int x = 0; x < p_size.x; x++) { + for (int y = 0; y < p_size.y; y++) { + Vector2i coords = p_atlas_coords + Vector2i(x, y); + ERR_FAIL_COND_MSG(tiles.has(coords), vformat("Cannot create tile at position %s with size %s. Already a tile present at %s.", p_atlas_coords, p_size, coords)); + } + } - int new_scene_id = p_id_override >= 0 ? p_id_override : next_scene_id; + // Create and resize the tile. + tiles.insert(p_atlas_coords, TileSetAtlasSource::TileAlternativesData()); + tiles_ids.append(p_atlas_coords); + tiles_ids.sort(); - scenes[new_scene_id] = SceneData(); - scenes_ids.append(new_scene_id); - scenes_ids.sort(); - set_scene_tile_scene(new_scene_id, p_packed_scene); - _compute_next_alternative_id(); + tiles[p_atlas_coords].size_in_atlas = p_size; + tiles[p_atlas_coords].alternatives[0] = memnew(TileData); + tiles[p_atlas_coords].alternatives[0]->set_tile_set(tile_set); + tiles[p_atlas_coords].alternatives[0]->set_allow_transform(false); + tiles[p_atlas_coords].alternatives[0]->connect("changed", callable_mp((Resource *)this, &TileSetAtlasSource::emit_changed)); + tiles[p_atlas_coords].alternatives[0]->notify_property_list_changed(); + tiles[p_atlas_coords].alternatives_ids.append(0); - emit_signal("changed"); + // Add all covered positions to the mapping cache + for (int x = 0; x < p_size.x; x++) { + for (int y = 0; y < p_size.y; y++) { + Vector2i coords = p_atlas_coords + Vector2i(x, y); + _coords_mapping_cache[coords] = p_atlas_coords; + } + } - return new_scene_id; + emit_signal("changed"); } -void TileSetScenesCollectionSource::set_scene_tile_id(int p_id, int p_new_id) { - ERR_FAIL_COND(p_new_id < 0); - ERR_FAIL_COND(!has_scene_tile_id(p_id)); - ERR_FAIL_COND(has_scene_tile_id(p_new_id)); +void TileSetAtlasSource::remove_tile(Vector2i p_atlas_coords) { + ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords))); - scenes[p_new_id] = SceneData(); - scenes[p_new_id] = scenes[p_id]; - scenes_ids.append(p_new_id); - scenes_ids.sort(); + // Remove all covered positions from the mapping cache + Size2i size = tiles[p_atlas_coords].size_in_atlas; - _compute_next_alternative_id(); + for (int x = 0; x < size.x; x++) { + for (int y = 0; y < size.y; y++) { + Vector2i coords = p_atlas_coords + Vector2i(x, y); + _coords_mapping_cache.erase(coords); + } + } - scenes.erase(p_id); - scenes_ids.erase(p_id); + // Free tile data. + for (Map::Element *E_tile_data = tiles[p_atlas_coords].alternatives.front(); E_tile_data; E_tile_data = E_tile_data->next()) { + memdelete(E_tile_data->get()); + } + + // Delete the tile + tiles.erase(p_atlas_coords); + tiles_ids.erase(p_atlas_coords); + tiles_ids.sort(); emit_signal("changed"); } -void TileSetScenesCollectionSource::set_scene_tile_scene(int p_id, Ref p_packed_scene) { - ERR_FAIL_COND(!scenes.has(p_id)); - if (p_packed_scene.is_valid()) { - // Make sure we have a root node. Supposed to be at 0 index because find_node_by_path() does not seem to work. - ERR_FAIL_COND(!p_packed_scene->get_state().is_valid()); - ERR_FAIL_COND(p_packed_scene->get_state()->get_node_count() < 1); - - // Check if it extends CanvasItem. - String type = p_packed_scene->get_state()->get_node_type(0); - bool extends_correct_class = ClassDB::is_parent_class(type, "Control") || ClassDB::is_parent_class(type, "Node2D"); - ERR_FAIL_COND_MSG(!extends_correct_class, vformat("Invalid PackedScene for TileSetScenesCollectionSource: %s. Root node should extend Control or Node2D.", p_packed_scene->get_path())); +bool TileSetAtlasSource::has_tile(Vector2i p_atlas_coords) const { + return tiles.has(p_atlas_coords); +} - scenes[p_id].scene = p_packed_scene; - } else { - scenes[p_id].scene = Ref(); +Vector2i TileSetAtlasSource::get_tile_at_coords(Vector2i p_atlas_coords) const { + if (!_coords_mapping_cache.has(p_atlas_coords)) { + return INVALID_ATLAS_COORDS; } - emit_signal("changed"); -} -Ref TileSetScenesCollectionSource::get_scene_tile_scene(int p_id) const { - ERR_FAIL_COND_V(!scenes.has(p_id), Ref()); - return scenes[p_id].scene; + return _coords_mapping_cache[p_atlas_coords]; } -void TileSetScenesCollectionSource::set_scene_tile_display_placeholder(int p_id, bool p_display_placeholder) { - ERR_FAIL_COND(!scenes.has(p_id)); +Vector2i TileSetAtlasSource::get_tile_size_in_atlas(Vector2i p_atlas_coords) const { + ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), Vector2i(-1, -1), vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords))); - scenes[p_id].display_placeholder = p_display_placeholder; + return tiles[p_atlas_coords].size_in_atlas; +} - emit_signal("changed"); +int TileSetAtlasSource::get_tiles_count() const { + return tiles_ids.size(); } -bool TileSetScenesCollectionSource::get_scene_tile_display_placeholder(int p_id) const { - ERR_FAIL_COND_V(!scenes.has(p_id), false); - return scenes[p_id].display_placeholder; +Vector2i TileSetAtlasSource::get_tile_id(int p_index) const { + ERR_FAIL_INDEX_V(p_index, tiles_ids.size(), TileSetSource::INVALID_ATLAS_COORDS); + return tiles_ids[p_index]; } -void TileSetScenesCollectionSource::remove_scene_tile(int p_id) { - ERR_FAIL_COND(!scenes.has(p_id)); +Rect2i TileSetAtlasSource::get_tile_texture_region(Vector2i p_atlas_coords) const { + ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), Rect2i(), vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords))); - scenes.erase(p_id); - scenes_ids.erase(p_id); - emit_signal("changed"); -} + Vector2i size_in_atlas = tiles[p_atlas_coords].size_in_atlas; + Vector2 region_size = texture_region_size * size_in_atlas + separation * (size_in_atlas - Vector2i(1, 1)); -int TileSetScenesCollectionSource::get_next_scene_tile_id() const { - return next_scene_id; -} + Vector2 origin = margins + (p_atlas_coords * (texture_region_size + separation)); -bool TileSetScenesCollectionSource::_set(const StringName &p_name, const Variant &p_value) { - Vector components = String(p_name).split("/", true, 2); + return Rect2(origin, region_size); + ; +} - if (components.size() >= 2 && components[0] == "scenes" && components[1].is_valid_int()) { - int scene_id = components[1].to_int(); - if (components.size() >= 3 && components[2] == "scene") { - if (has_scene_tile_id(scene_id)) { - set_scene_tile_scene(scene_id, p_value); - } else { - create_scene_tile(p_value, scene_id); - } - return true; - } else if (components.size() >= 3 && components[2] == "display_placeholder") { - if (!has_scene_tile_id(scene_id)) { - create_scene_tile(p_value, scene_id); - } +Vector2i TileSetAtlasSource::get_tile_effective_texture_offset(Vector2i p_atlas_coords, int p_alternative_tile) const { + ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), Vector2i(), vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords))); + ERR_FAIL_COND_V_MSG(!has_alternative_tile(p_atlas_coords, p_alternative_tile), Vector2i(), vformat("TileSetAtlasSource has no alternative tile with id %d at %s.", p_alternative_tile, String(p_atlas_coords))); + ERR_FAIL_COND_V(!tile_set, Vector2i()); - return true; - } + Vector2 margin = (get_tile_texture_region(p_atlas_coords).size - tile_set->get_tile_size()) / 2; + margin = Vector2i(MAX(0, margin.x), MAX(0, margin.y)); + Vector2i effective_texture_offset = Object::cast_to(get_tile_data(p_atlas_coords, p_alternative_tile))->get_texture_offset(); + if (ABS(effective_texture_offset.x) > margin.x || ABS(effective_texture_offset.y) > margin.y) { + effective_texture_offset.x = CLAMP(effective_texture_offset.x, -margin.x, margin.x); + effective_texture_offset.y = CLAMP(effective_texture_offset.y, -margin.y, margin.y); } - return false; + return effective_texture_offset; } -bool TileSetScenesCollectionSource::_get(const StringName &p_name, Variant &r_ret) const { - Vector components = String(p_name).split("/", true, 2); +bool TileSetAtlasSource::can_move_tile_in_atlas(Vector2i p_atlas_coords, Vector2i p_new_atlas_coords, Vector2i p_new_size) const { + ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), false, vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords))); - if (components.size() >= 2 && components[0] == "scenes" && components[1].is_valid_int() && scenes.has(components[1].to_int())) { - if (components.size() >= 3 && components[2] == "scene") { - r_ret = scenes[components[1].to_int()].scene; - return true; - } else if (components.size() >= 3 && components[2] == "display_placeholder") { - r_ret = scenes[components[1].to_int()].scene; - return true; - } + Vector2i new_atlas_coords = (p_new_atlas_coords != INVALID_ATLAS_COORDS) ? p_new_atlas_coords : p_atlas_coords; + if (new_atlas_coords.x < 0 || new_atlas_coords.y < 0) { + return false; } - return false; -} + Vector2i size = (p_new_size != Vector2i(-1, -1)) ? p_new_size : tiles[p_atlas_coords].size_in_atlas; + ERR_FAIL_COND_V(size.x <= 0 || size.y <= 0, false); -void TileSetScenesCollectionSource::_get_property_list(List *p_list) const { - for (int i = 0; i < scenes_ids.size(); i++) { - p_list->push_back(PropertyInfo(Variant::OBJECT, vformat("scenes/%d/scene", scenes_ids[i]), PROPERTY_HINT_RESOURCE_TYPE, "TileSetScenesCollectionSource")); + Size2i grid_size = get_atlas_grid_size(); + if (new_atlas_coords.x + size.x > grid_size.x || new_atlas_coords.y + size.y > grid_size.y) { + return false; + } - PropertyInfo property_info = PropertyInfo(Variant::BOOL, vformat("scenes/%d/display_placeholder", scenes_ids[i])); - if (scenes[scenes_ids[i]].display_placeholder == false) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; + Rect2i new_rect = Rect2i(new_atlas_coords, size); + // Check if the new tile can fit in the new rect. + for (int x = new_rect.position.x; x < new_rect.get_end().x; x++) { + for (int y = new_rect.position.y; y < new_rect.get_end().y; y++) { + Vector2i coords = get_tile_at_coords(Vector2i(x, y)); + if (coords != p_atlas_coords && coords != TileSetSource::INVALID_ATLAS_COORDS) { + return false; + } } - p_list->push_back(property_info); } + + return true; } -void TileSetScenesCollectionSource::_bind_methods() { - // Base tiles - ClassDB::bind_method(D_METHOD("get_tiles_count"), &TileSetScenesCollectionSource::get_tiles_count); - ClassDB::bind_method(D_METHOD("get_tile_id", "index"), &TileSetScenesCollectionSource::get_tile_id); - ClassDB::bind_method(D_METHOD("has_tile", "atlas_coords"), &TileSetScenesCollectionSource::has_tile); +void TileSetAtlasSource::move_tile_in_atlas(Vector2i p_atlas_coords, Vector2i p_new_atlas_coords, Vector2i p_new_size) { + bool can_move = can_move_tile_in_atlas(p_atlas_coords, p_new_atlas_coords, p_new_size); + ERR_FAIL_COND_MSG(!can_move, vformat("Cannot move tile at position %s with size %s. Tile already present.", p_new_atlas_coords, p_new_size)); - // Alternative tiles - ClassDB::bind_method(D_METHOD("get_alternative_tiles_count", "atlas_coords"), &TileSetScenesCollectionSource::get_alternative_tiles_count); - ClassDB::bind_method(D_METHOD("get_alternative_tile_id", "atlas_coords", "index"), &TileSetScenesCollectionSource::get_alternative_tile_id); - ClassDB::bind_method(D_METHOD("has_alternative_tile", "atlas_coords", "alternative_tile"), &TileSetScenesCollectionSource::has_alternative_tile); + // Compute the actual new rect from arguments. + Vector2i new_atlas_coords = (p_new_atlas_coords != INVALID_ATLAS_COORDS) ? p_new_atlas_coords : p_atlas_coords; + Vector2i size = (p_new_size != Vector2i(-1, -1)) ? p_new_size : tiles[p_atlas_coords].size_in_atlas; - ClassDB::bind_method(D_METHOD("get_scene_tiles_count"), &TileSetScenesCollectionSource::get_scene_tiles_count); - ClassDB::bind_method(D_METHOD("get_scene_tile_id", "index"), &TileSetScenesCollectionSource::get_scene_tile_id); - ClassDB::bind_method(D_METHOD("has_scene_tile_id", "id"), &TileSetScenesCollectionSource::has_scene_tile_id); - ClassDB::bind_method(D_METHOD("create_scene_tile", "packed_scene", "id_override"), &TileSetScenesCollectionSource::create_scene_tile, DEFVAL(-1)); - ClassDB::bind_method(D_METHOD("set_scene_tile_id", "id", "new_id"), &TileSetScenesCollectionSource::set_scene_tile_id); - ClassDB::bind_method(D_METHOD("set_scene_tile_scene", "id", "packed_scene"), &TileSetScenesCollectionSource::set_scene_tile_scene); - ClassDB::bind_method(D_METHOD("get_scene_tile_scene", "id"), &TileSetScenesCollectionSource::get_scene_tile_scene); - ClassDB::bind_method(D_METHOD("set_scene_tile_display_placeholder", "id", "display_placeholder"), &TileSetScenesCollectionSource::set_scene_tile_display_placeholder); - ClassDB::bind_method(D_METHOD("get_scene_tile_display_placeholder", "id"), &TileSetScenesCollectionSource::get_scene_tile_display_placeholder); - ClassDB::bind_method(D_METHOD("remove_scene_tile", "id"), &TileSetScenesCollectionSource::remove_scene_tile); - ClassDB::bind_method(D_METHOD("get_next_scene_tile_id"), &TileSetScenesCollectionSource::get_next_scene_tile_id); -} + if (new_atlas_coords == p_atlas_coords && size == tiles[p_atlas_coords].size_in_atlas) { + return; + } -/////////////////////////////// TileData ////////////////////////////////////// + // Remove all covered positions from the mapping cache. + Size2i old_size = tiles[p_atlas_coords].size_in_atlas; + for (int x = 0; x < old_size.x; x++) { + for (int y = 0; y < old_size.y; y++) { + Vector2i coords = p_atlas_coords + Vector2i(x, y); + _coords_mapping_cache.erase(coords); + } + } -void TileData::set_tile_set(const TileSet *p_tile_set) { - tile_set = p_tile_set; - if (tile_set) { - occluders.resize(tile_set->get_occlusion_layers_count()); - physics.resize(tile_set->get_physics_layers_count()); - navigation.resize(tile_set->get_navigation_layers_count()); - custom_data.resize(tile_set->get_custom_data_layers_count()); + // Move the tile and update its size. + if (new_atlas_coords != p_atlas_coords) { + tiles[new_atlas_coords] = tiles[p_atlas_coords]; + tiles.erase(p_atlas_coords); + + tiles_ids.erase(p_atlas_coords); + tiles_ids.append(new_atlas_coords); + tiles_ids.sort(); } - notify_property_list_changed(); -} + tiles[new_atlas_coords].size_in_atlas = size; -void TileData::notify_tile_data_properties_should_change() { - occluders.resize(tile_set->get_occlusion_layers_count()); - physics.resize(tile_set->get_physics_layers_count()); - for (int bit_index = 0; bit_index < 16; bit_index++) { - if (terrain_set < 0 || terrain_peering_bits[bit_index] >= tile_set->get_terrains_count(terrain_set)) { - terrain_peering_bits[bit_index] = -1; + // Add all covered positions to the mapping cache again. + for (int x = 0; x < size.x; x++) { + for (int y = 0; y < size.y; y++) { + Vector2i coords = new_atlas_coords + Vector2i(x, y); + _coords_mapping_cache[coords] = new_atlas_coords; } } - navigation.resize(tile_set->get_navigation_layers_count()); - // Convert custom data to the new type. - custom_data.resize(tile_set->get_custom_data_layers_count()); - for (int i = 0; i < custom_data.size(); i++) { - if (custom_data[i].get_type() != tile_set->get_custom_data_type(i)) { - Variant new_val; - Callable::CallError error; - if (Variant::can_convert(custom_data[i].get_type(), tile_set->get_custom_data_type(i))) { - const Variant *args[] = { &custom_data[i] }; - Variant::construct(tile_set->get_custom_data_type(i), new_val, args, 1, error); - } else { - Variant::construct(tile_set->get_custom_data_type(i), new_val, nullptr, 0, error); - } - custom_data.write[i] = new_val; + emit_signal("changed"); +} + +bool TileSetAtlasSource::has_tiles_outside_texture() { + Vector2i grid_size = get_atlas_grid_size(); + Vector to_remove; + + for (Map::Element *E = tiles.front(); E; E = E->next()) { + if (E->key().x >= grid_size.x || E->key().y >= grid_size.y) { + return true; } } - notify_property_list_changed(); - emit_signal("changed"); + return false; } -void TileData::reset_state() { - occluders.clear(); - physics.clear(); - navigation.clear(); - custom_data.clear(); -} +void TileSetAtlasSource::clear_tiles_outside_texture() { + Vector2i grid_size = get_atlas_grid_size(); + Vector to_remove; -void TileData::set_allow_transform(bool p_allow_transform) { - allow_transform = p_allow_transform; -} + for (Map::Element *E = tiles.front(); E; E = E->next()) { + if (E->key().x >= grid_size.x || E->key().y >= grid_size.y) { + to_remove.append(E->key()); + } + } -bool TileData::is_allowing_transform() const { - return allow_transform; + for (int i = 0; i < to_remove.size(); i++) { + remove_tile(to_remove[i]); + } } -// Rendering -void TileData::set_flip_h(bool p_flip_h) { - ERR_FAIL_COND_MSG(!allow_transform && p_flip_h, "Transform is only allowed for alternative tiles (with its alternative_id != 0)"); - flip_h = p_flip_h; - emit_signal("changed"); -} -bool TileData::get_flip_h() const { - return flip_h; -} +int TileSetAtlasSource::create_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_id_override) { + ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), -1, vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords))); + ERR_FAIL_COND_V_MSG(p_alternative_id_override >= 0 && tiles[p_atlas_coords].alternatives.has(p_alternative_id_override), -1, vformat("Cannot create alternative tile. Another alternative exists with id %d.", p_alternative_id_override)); -void TileData::set_flip_v(bool p_flip_v) { - ERR_FAIL_COND_MSG(!allow_transform && p_flip_v, "Transform is only allowed for alternative tiles (with its alternative_id != 0)"); - flip_v = p_flip_v; - emit_signal("changed"); -} + int new_alternative_id = p_alternative_id_override >= 0 ? p_alternative_id_override : tiles[p_atlas_coords].next_alternative_id; -bool TileData::get_flip_v() const { - return flip_v; -} + tiles[p_atlas_coords].alternatives[new_alternative_id] = memnew(TileData); + tiles[p_atlas_coords].alternatives[new_alternative_id]->set_tile_set(tile_set); + tiles[p_atlas_coords].alternatives[new_alternative_id]->set_allow_transform(true); + tiles[p_atlas_coords].alternatives[new_alternative_id]->notify_property_list_changed(); + tiles[p_atlas_coords].alternatives_ids.append(new_alternative_id); + tiles[p_atlas_coords].alternatives_ids.sort(); + _compute_next_alternative_id(p_atlas_coords); -void TileData::set_transpose(bool p_transpose) { - ERR_FAIL_COND_MSG(!allow_transform && p_transpose, "Transform is only allowed for alternative tiles (with its alternative_id != 0)"); - transpose = p_transpose; emit_signal("changed"); -} -bool TileData::get_transpose() const { - return transpose; -} -void TileData::set_texture_offset(Vector2i p_texture_offset) { - tex_offset = p_texture_offset; - emit_signal("changed"); + return new_alternative_id; } -Vector2i TileData::get_texture_offset() const { - return tex_offset; -} +void TileSetAtlasSource::remove_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_tile) { + ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords))); + ERR_FAIL_COND_MSG(!tiles[p_atlas_coords].alternatives.has(p_alternative_tile), vformat("TileSetAtlasSource has no alternative with id %d for tile coords %s.", p_alternative_tile, String(p_atlas_coords))); + ERR_FAIL_COND_MSG(p_alternative_tile == 0, "Cannot remove the alternative with id 0, the base tile alternative cannot be removed."); -void TileData::tile_set_material(Ref p_material) { - material = p_material; - emit_signal("changed"); -} -Ref TileData::tile_get_material() const { - return material; -} + memdelete(tiles[p_atlas_coords].alternatives[p_alternative_tile]); + tiles[p_atlas_coords].alternatives.erase(p_alternative_tile); + tiles[p_atlas_coords].alternatives_ids.erase(p_alternative_tile); + tiles[p_atlas_coords].alternatives_ids.sort(); -void TileData::set_modulate(Color p_modulate) { - modulate = p_modulate; emit_signal("changed"); } -Color TileData::get_modulate() const { - return modulate; -} -void TileData::set_z_index(int p_z_index) { - z_index = p_z_index; - emit_signal("changed"); -} -int TileData::get_z_index() const { - return z_index; -} +void TileSetAtlasSource::set_alternative_tile_id(const Vector2i p_atlas_coords, int p_alternative_tile, int p_new_id) { + ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords))); + ERR_FAIL_COND_MSG(!tiles[p_atlas_coords].alternatives.has(p_alternative_tile), vformat("TileSetAtlasSource has no alternative with id %d for tile coords %s.", p_alternative_tile, String(p_atlas_coords))); + ERR_FAIL_COND_MSG(p_alternative_tile == 0, "Cannot change the alternative with id 0, the base tile alternative cannot be modified."); -void TileData::set_y_sort_origin(int p_y_sort_origin) { - y_sort_origin = p_y_sort_origin; - emit_signal("changed"); -} -int TileData::get_y_sort_origin() const { - return y_sort_origin; -} + ERR_FAIL_COND_MSG(tiles[p_atlas_coords].alternatives.has(p_new_id), vformat("TileSetAtlasSource has already an alternative with id %d at %s.", p_new_id, String(p_atlas_coords))); + + tiles[p_atlas_coords].alternatives[p_new_id] = tiles[p_atlas_coords].alternatives[p_alternative_tile]; + tiles[p_atlas_coords].alternatives_ids.append(p_new_id); + + tiles[p_atlas_coords].alternatives.erase(p_alternative_tile); + tiles[p_atlas_coords].alternatives_ids.erase(p_alternative_tile); + tiles[p_atlas_coords].alternatives_ids.sort(); -void TileData::set_occluder(int p_layer_id, Ref p_occluder_polygon) { - ERR_FAIL_INDEX(p_layer_id, occluders.size()); - occluders.write[p_layer_id] = p_occluder_polygon; emit_signal("changed"); } -Ref TileData::get_occluder(int p_layer_id) const { - ERR_FAIL_INDEX_V(p_layer_id, occluders.size(), Ref()); - return occluders[p_layer_id]; +bool TileSetAtlasSource::has_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_tile) const { + ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), false, vformat("The TileSetAtlasSource atlas has no tile at %s.", String(p_atlas_coords))); + return tiles[p_atlas_coords].alternatives.has(p_alternative_tile); } -// Physics -int TileData::get_collision_shapes_count(int p_layer_id) const { - ERR_FAIL_INDEX_V(p_layer_id, physics.size(), 0); - return physics[p_layer_id].shapes.size(); +int TileSetAtlasSource::get_next_alternative_tile_id(const Vector2i p_atlas_coords) const { + ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), -1, vformat("The TileSetAtlasSource atlas has no tile at %s.", String(p_atlas_coords))); + return tiles[p_atlas_coords].next_alternative_id; } -void TileData::set_collision_shapes_count(int p_layer_id, int p_shapes_count) { - ERR_FAIL_INDEX(p_layer_id, physics.size()); - ERR_FAIL_COND(p_shapes_count < 0); - physics.write[p_layer_id].shapes.resize(p_shapes_count); - notify_property_list_changed(); - emit_signal("changed"); +int TileSetAtlasSource::get_alternative_tiles_count(const Vector2i p_atlas_coords) const { + ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), -1, vformat("The TileSetAtlasSource atlas has no tile at %s.", String(p_atlas_coords))); + return tiles[p_atlas_coords].alternatives_ids.size(); } -void TileData::add_collision_shape(int p_layer_id) { - ERR_FAIL_INDEX(p_layer_id, physics.size()); - physics.write[p_layer_id].shapes.push_back(PhysicsLayerTileData::ShapeTileData()); - emit_signal("changed"); +int TileSetAtlasSource::get_alternative_tile_id(const Vector2i p_atlas_coords, int p_index) const { + ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), -1, vformat("The TileSetAtlasSource atlas has no tile at %s.", String(p_atlas_coords))); + ERR_FAIL_INDEX_V(p_index, tiles[p_atlas_coords].alternatives_ids.size(), -1); + + return tiles[p_atlas_coords].alternatives_ids[p_index]; } -void TileData::remove_collision_shape(int p_layer_id, int p_shape_index) { - ERR_FAIL_INDEX(p_layer_id, physics.size()); - ERR_FAIL_INDEX(p_shape_index, physics[p_layer_id].shapes.size()); - physics.write[p_layer_id].shapes.remove(p_shape_index); - emit_signal("changed"); +Object *TileSetAtlasSource::get_tile_data(const Vector2i p_atlas_coords, int p_alternative_tile) const { + ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), nullptr, vformat("The TileSetAtlasSource atlas has no tile at %s.", String(p_atlas_coords))); + ERR_FAIL_COND_V_MSG(!tiles[p_atlas_coords].alternatives.has(p_alternative_tile), nullptr, vformat("TileSetAtlasSource has no alternative with id %d for tile coords %s.", p_alternative_tile, String(p_atlas_coords))); + + return tiles[p_atlas_coords].alternatives[p_alternative_tile]; } -void TileData::set_collision_shape_shape(int p_layer_id, int p_shape_index, Ref p_shape) { - ERR_FAIL_INDEX(p_layer_id, physics.size()); - ERR_FAIL_INDEX(p_shape_index, physics[p_layer_id].shapes.size()); - physics.write[p_layer_id].shapes.write[p_shape_index].shape = p_shape; - emit_signal("changed"); +void TileSetAtlasSource::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_texture", "texture"), &TileSetAtlasSource::set_texture); + ClassDB::bind_method(D_METHOD("get_texture"), &TileSetAtlasSource::get_texture); + ClassDB::bind_method(D_METHOD("set_margins", "margins"), &TileSetAtlasSource::set_margins); + ClassDB::bind_method(D_METHOD("get_margins"), &TileSetAtlasSource::get_margins); + ClassDB::bind_method(D_METHOD("set_separation", "separation"), &TileSetAtlasSource::set_separation); + ClassDB::bind_method(D_METHOD("get_separation"), &TileSetAtlasSource::get_separation); + ClassDB::bind_method(D_METHOD("set_texture_region_size", "texture_region_size"), &TileSetAtlasSource::set_texture_region_size); + ClassDB::bind_method(D_METHOD("get_texture_region_size"), &TileSetAtlasSource::get_texture_region_size); + + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D", PROPERTY_USAGE_NOEDITOR), "set_texture", "get_texture"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "margins", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_margins", "get_margins"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "separation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_separation", "get_separation"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "tile_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_texture_region_size", "get_texture_region_size"); + + // Base tiles + ClassDB::bind_method(D_METHOD("create_tile", "atlas_coords", "size"), &TileSetAtlasSource::create_tile, DEFVAL(Vector2i(1, 1))); + ClassDB::bind_method(D_METHOD("remove_tile", "atlas_coords"), &TileSetAtlasSource::remove_tile); // Remove a tile. If p_tile_key.alternative_tile if different from 0, remove the alternative + ClassDB::bind_method(D_METHOD("has_tile", "atlas_coords"), &TileSetAtlasSource::has_tile); + ClassDB::bind_method(D_METHOD("can_move_tile_in_atlas", "atlas_coords", "new_atlas_coords", "new_size"), &TileSetAtlasSource::can_move_tile_in_atlas, DEFVAL(INVALID_ATLAS_COORDS), DEFVAL(Vector2i(-1, -1))); + ClassDB::bind_method(D_METHOD("move_tile_in_atlas", "atlas_coords", "new_atlas_coords", "new_size"), &TileSetAtlasSource::move_tile_in_atlas, DEFVAL(INVALID_ATLAS_COORDS), DEFVAL(Vector2i(-1, -1))); + ClassDB::bind_method(D_METHOD("get_tile_size_in_atlas", "atlas_coords"), &TileSetAtlasSource::get_tile_size_in_atlas); + + ClassDB::bind_method(D_METHOD("get_tiles_count"), &TileSetAtlasSource::get_tiles_count); + ClassDB::bind_method(D_METHOD("get_tile_id", "index"), &TileSetAtlasSource::get_tile_id); + + ClassDB::bind_method(D_METHOD("get_tile_at_coords", "atlas_coords"), &TileSetAtlasSource::get_tile_at_coords); + + // Alternative tiles + ClassDB::bind_method(D_METHOD("create_alternative_tile", "atlas_coords", "alternative_id_override"), &TileSetAtlasSource::create_alternative_tile, DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("remove_alternative_tile", "atlas_coords", "alternative_tile"), &TileSetAtlasSource::remove_alternative_tile); + ClassDB::bind_method(D_METHOD("set_alternative_tile_id", "atlas_coords", "alternative_tile", "new_id"), &TileSetAtlasSource::set_alternative_tile_id); + ClassDB::bind_method(D_METHOD("has_alternative_tile", "atlas_coords", "alternative_tile"), &TileSetAtlasSource::has_alternative_tile); + ClassDB::bind_method(D_METHOD("get_next_alternative_tile_id", "atlas_coords"), &TileSetAtlasSource::get_next_alternative_tile_id); + + ClassDB::bind_method(D_METHOD("get_alternative_tiles_count", "atlas_coords"), &TileSetAtlasSource::get_alternative_tiles_count); + ClassDB::bind_method(D_METHOD("get_alternative_tile_id", "atlas_coords", "index"), &TileSetAtlasSource::get_alternative_tile_id); + + ClassDB::bind_method(D_METHOD("get_tile_data", "atlas_coords", "index"), &TileSetAtlasSource::get_tile_data); + + // Helpers. + ClassDB::bind_method(D_METHOD("get_atlas_grid_size"), &TileSetAtlasSource::get_atlas_grid_size); + ClassDB::bind_method(D_METHOD("has_tiles_outside_texture"), &TileSetAtlasSource::has_tiles_outside_texture); + ClassDB::bind_method(D_METHOD("clear_tiles_outside_texture"), &TileSetAtlasSource::clear_tiles_outside_texture); + ClassDB::bind_method(D_METHOD("get_tile_texture_region", "atlas_coords"), &TileSetAtlasSource::get_tile_texture_region); } -Ref TileData::get_collision_shape_shape(int p_layer_id, int p_shape_index) const { - ERR_FAIL_INDEX_V(p_layer_id, physics.size(), Ref()); - ERR_FAIL_INDEX_V(p_shape_index, physics[p_layer_id].shapes.size(), Ref()); - return physics[p_layer_id].shapes[p_shape_index].shape; +TileSetAtlasSource::~TileSetAtlasSource() { + // Free everything needed. + for (Map::Element *E_alternatives = tiles.front(); E_alternatives; E_alternatives = E_alternatives->next()) { + for (Map::Element *E_tile_data = E_alternatives->get().alternatives.front(); E_tile_data; E_tile_data = E_tile_data->next()) { + memdelete(E_tile_data->get()); + } + } } -void TileData::set_collision_shape_one_way(int p_layer_id, int p_shape_index, bool p_one_way) { - ERR_FAIL_INDEX(p_layer_id, physics.size()); - ERR_FAIL_INDEX(p_shape_index, physics[p_layer_id].shapes.size()); - physics.write[p_layer_id].shapes.write[p_shape_index].one_way = p_one_way; - emit_signal("changed"); +TileData *TileSetAtlasSource::_get_atlas_tile_data(Vector2i p_atlas_coords, int p_alternative_tile) { + ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), nullptr, vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords))); + ERR_FAIL_COND_V_MSG(!tiles[p_atlas_coords].alternatives.has(p_alternative_tile), nullptr, vformat("TileSetAtlasSource has no alternative with id %d for tile coords %s.", p_alternative_tile, String(p_atlas_coords))); + + return tiles[p_atlas_coords].alternatives[p_alternative_tile]; } -bool TileData::is_collision_shape_one_way(int p_layer_id, int p_shape_index) const { - ERR_FAIL_INDEX_V(p_layer_id, physics.size(), false); - ERR_FAIL_INDEX_V(p_shape_index, physics[p_layer_id].shapes.size(), false); - return physics[p_layer_id].shapes[p_shape_index].one_way; +const TileData *TileSetAtlasSource::_get_atlas_tile_data(Vector2i p_atlas_coords, int p_alternative_tile) const { + ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), nullptr, vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords))); + ERR_FAIL_COND_V_MSG(!tiles[p_atlas_coords].alternatives.has(p_alternative_tile), nullptr, vformat("TileSetAtlasSource has no alternative with id %d for tile coords %s.", p_alternative_tile, String(p_atlas_coords))); + + return tiles[p_atlas_coords].alternatives[p_alternative_tile]; } -void TileData::set_collision_shape_one_way_margin(int p_layer_id, int p_shape_index, float p_one_way_margin) { - ERR_FAIL_INDEX(p_layer_id, physics.size()); - ERR_FAIL_INDEX(p_shape_index, physics[p_layer_id].shapes.size()); - physics.write[p_layer_id].shapes.write[p_shape_index].one_way_margin = p_one_way_margin; - emit_signal("changed"); +void TileSetAtlasSource::_compute_next_alternative_id(const Vector2i p_atlas_coords) { + ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords))); + + while (tiles[p_atlas_coords].alternatives.has(tiles[p_atlas_coords].next_alternative_id)) { + tiles[p_atlas_coords].next_alternative_id = (tiles[p_atlas_coords].next_alternative_id % 1073741823) + 1; // 2 ** 30 + }; } -float TileData::get_collision_shape_one_way_margin(int p_layer_id, int p_shape_index) const { - ERR_FAIL_INDEX_V(p_layer_id, physics.size(), 0.0); - ERR_FAIL_INDEX_V(p_shape_index, physics[p_layer_id].shapes.size(), 0.0); - return physics[p_layer_id].shapes[p_shape_index].one_way_margin; +/////////////////////////////// TileSetScenesCollectionSource ////////////////////////////////////// + +void TileSetScenesCollectionSource::_compute_next_alternative_id() { + while (scenes.has(next_scene_id)) { + next_scene_id = (next_scene_id % 1073741823) + 1; // 2 ** 30 + }; } -// Terrain -void TileData::set_terrain_set(int p_terrain_set) { - ERR_FAIL_COND(p_terrain_set < -1); - if (tile_set) { - ERR_FAIL_COND(p_terrain_set >= tile_set->get_terrain_sets_count()); - } - terrain_set = p_terrain_set; - notify_property_list_changed(); - emit_signal("changed"); +int TileSetScenesCollectionSource::get_tiles_count() const { + return 1; } -int TileData::get_terrain_set() const { - return terrain_set; +Vector2i TileSetScenesCollectionSource::get_tile_id(int p_tile_index) const { + ERR_FAIL_COND_V(p_tile_index != 0, TileSetSource::INVALID_ATLAS_COORDS); + return Vector2i(); } -void TileData::set_peering_bit_terrain(TileSet::CellNeighbor p_peering_bit, int p_terrain_index) { - ERR_FAIL_INDEX(p_peering_bit, TileSet::CELL_NEIGHBOR_MAX); - ERR_FAIL_COND(p_terrain_index < -1); - if (tile_set) { - ERR_FAIL_COND(p_terrain_index >= tile_set->get_terrains_count(terrain_set)); - ERR_FAIL_COND(!is_valid_peering_bit_terrain(p_peering_bit)); - } - terrain_peering_bits[p_peering_bit] = p_terrain_index; - emit_signal("changed"); +bool TileSetScenesCollectionSource::has_tile(Vector2i p_atlas_coords) const { + return p_atlas_coords == Vector2i(); } -int TileData::get_peering_bit_terrain(TileSet::CellNeighbor p_peering_bit) const { - ERR_FAIL_INDEX_V(p_peering_bit, TileSet::CELL_NEIGHBOR_MAX, -1); - return terrain_peering_bits[p_peering_bit]; +int TileSetScenesCollectionSource::get_alternative_tiles_count(const Vector2i p_atlas_coords) const { + return scenes_ids.size(); } -bool TileData::is_valid_peering_bit_terrain(TileSet::CellNeighbor p_peering_bit) const { - ERR_FAIL_COND_V(!tile_set, false); +int TileSetScenesCollectionSource::get_alternative_tile_id(const Vector2i p_atlas_coords, int p_index) const { + ERR_FAIL_COND_V(p_atlas_coords != Vector2i(), TileSetSource::INVALID_TILE_ALTERNATIVE); + ERR_FAIL_INDEX_V(p_index, scenes_ids.size(), TileSetSource::INVALID_TILE_ALTERNATIVE); - return tile_set->is_valid_peering_bit_terrain(terrain_set, p_peering_bit); + return scenes_ids[p_index]; } -// Navigation -void TileData::set_navigation_polygon(int p_layer_id, Ref p_navigation_polygon) { - ERR_FAIL_INDEX(p_layer_id, navigation.size()); - navigation.write[p_layer_id] = p_navigation_polygon; - emit_signal("changed"); +bool TileSetScenesCollectionSource::has_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_tile) const { + ERR_FAIL_COND_V(p_atlas_coords != Vector2i(), false); + return scenes.has(p_alternative_tile); } -Ref TileData::get_navigation_polygon(int p_layer_id) const { - ERR_FAIL_INDEX_V(p_layer_id, navigation.size(), Ref()); - return navigation[p_layer_id]; -} +int TileSetScenesCollectionSource::create_scene_tile(Ref p_packed_scene, int p_id_override) { + ERR_FAIL_COND_V_MSG(p_id_override >= 0 && scenes.has(p_id_override), -1, vformat("Cannot create scene tile. Another scene tile exists with id %d.", p_id_override)); + + int new_scene_id = p_id_override >= 0 ? p_id_override : next_scene_id; + + scenes[new_scene_id] = SceneData(); + scenes_ids.append(new_scene_id); + scenes_ids.sort(); + set_scene_tile_scene(new_scene_id, p_packed_scene); + _compute_next_alternative_id(); -// Misc -void TileData::set_probability(float p_probability) { - ERR_FAIL_COND(p_probability <= 0.0); - probability = p_probability; emit_signal("changed"); -} -float TileData::get_probability() const { - return probability; -} -// Custom data -void TileData::set_custom_data(String p_layer_name, Variant p_value) { - ERR_FAIL_COND(!tile_set); - int p_layer_id = tile_set->get_custom_data_layer_by_name(p_layer_name); - ERR_FAIL_COND_MSG(p_layer_id < 0, vformat("TileSet has no layer with name: %s", p_layer_name)); - set_custom_data_by_layer_id(p_layer_id, p_value); + return new_scene_id; } -Variant TileData::get_custom_data(String p_layer_name) const { - ERR_FAIL_COND_V(!tile_set, Variant()); - int p_layer_id = tile_set->get_custom_data_layer_by_name(p_layer_name); - ERR_FAIL_COND_V_MSG(p_layer_id < 0, Variant(), vformat("TileSet has no layer with name: %s", p_layer_name)); - return get_custom_data_by_layer_id(p_layer_id); -} +void TileSetScenesCollectionSource::set_scene_tile_id(int p_id, int p_new_id) { + ERR_FAIL_COND(p_new_id < 0); + ERR_FAIL_COND(!has_scene_tile_id(p_id)); + ERR_FAIL_COND(has_scene_tile_id(p_new_id)); + + scenes[p_new_id] = SceneData(); + scenes[p_new_id] = scenes[p_id]; + scenes_ids.append(p_new_id); + scenes_ids.sort(); + + _compute_next_alternative_id(); + + scenes.erase(p_id); + scenes_ids.erase(p_id); -void TileData::set_custom_data_by_layer_id(int p_layer_id, Variant p_value) { - ERR_FAIL_INDEX(p_layer_id, custom_data.size()); - custom_data.write[p_layer_id] = p_value; emit_signal("changed"); } -Variant TileData::get_custom_data_by_layer_id(int p_layer_id) const { - ERR_FAIL_INDEX_V(p_layer_id, custom_data.size(), Variant()); - return custom_data[p_layer_id]; -} +void TileSetScenesCollectionSource::set_scene_tile_scene(int p_id, Ref p_packed_scene) { + ERR_FAIL_COND(!scenes.has(p_id)); + if (p_packed_scene.is_valid()) { + // Make sure we have a root node. Supposed to be at 0 index because find_node_by_path() does not seem to work. + ERR_FAIL_COND(!p_packed_scene->get_state().is_valid()); + ERR_FAIL_COND(p_packed_scene->get_state()->get_node_count() < 1); -bool TileData::_set(const StringName &p_name, const Variant &p_value) { - Vector components = String(p_name).split("/", true, 2); + // Check if it extends CanvasItem. + String type = p_packed_scene->get_state()->get_node_type(0); + bool extends_correct_class = ClassDB::is_parent_class(type, "Control") || ClassDB::is_parent_class(type, "Node2D"); + ERR_FAIL_COND_MSG(!extends_correct_class, vformat("Invalid PackedScene for TileSetScenesCollectionSource: %s. Root node should extend Control or Node2D.", p_packed_scene->get_path())); - if (components.size() == 2 && components[0].begins_with("occlusion_layer_") && components[0].trim_prefix("occlusion_layer_").is_valid_int()) { - // Occlusion layers. - int layer_index = components[0].trim_prefix("occlusion_layer_").to_int(); - ERR_FAIL_COND_V(layer_index < 0, false); - if (components[1] == "polygon") { - Ref polygon = p_value; - if (!polygon.is_valid()) { - return false; - } + scenes[p_id].scene = p_packed_scene; + } else { + scenes[p_id].scene = Ref(); + } + emit_signal("changed"); +} - if (layer_index >= occluders.size()) { - if (tile_set) { - return false; - } else { - occluders.resize(layer_index + 1); - } - } - set_occluder(layer_index, polygon); - return true; - } - } else if (components.size() >= 2 && components[0].begins_with("physics_layer_") && components[0].trim_prefix("physics_layer_").is_valid_int()) { - // Physics layers. - int layer_index = components[0].trim_prefix("physics_layer_").to_int(); - ERR_FAIL_COND_V(layer_index < 0, false); - if (components.size() == 2 && components[1] == "shapes_count") { - if (p_value.get_type() != Variant::INT) { - return false; - } +Ref TileSetScenesCollectionSource::get_scene_tile_scene(int p_id) const { + ERR_FAIL_COND_V(!scenes.has(p_id), Ref()); + return scenes[p_id].scene; +} - if (layer_index >= physics.size()) { - if (tile_set) { - return false; - } else { - physics.resize(layer_index + 1); - } - } - set_collision_shapes_count(layer_index, p_value); - return true; - } else if (components.size() == 3 && components[1].begins_with("shape_") && components[1].trim_prefix("shape_").is_valid_int()) { - int shape_index = components[1].trim_prefix("shape_").to_int(); - ERR_FAIL_COND_V(shape_index < 0, false); +void TileSetScenesCollectionSource::set_scene_tile_display_placeholder(int p_id, bool p_display_placeholder) { + ERR_FAIL_COND(!scenes.has(p_id)); - if (components[2] == "shape" || components[2] == "one_way" || components[2] == "one_way_margin") { - if (layer_index >= physics.size()) { - if (tile_set) { - return false; - } else { - physics.resize(layer_index + 1); - } - } + scenes[p_id].display_placeholder = p_display_placeholder; - if (shape_index >= physics[layer_index].shapes.size()) { - physics.write[layer_index].shapes.resize(shape_index + 1); - } - } - if (components[2] == "shape") { - Ref shape = p_value; - set_collision_shape_shape(layer_index, shape_index, shape); - return true; - } else if (components[2] == "one_way") { - set_collision_shape_one_way(layer_index, shape_index, p_value); - return true; - } else if (components[2] == "one_way_margin") { - set_collision_shape_one_way_margin(layer_index, shape_index, p_value); - return true; - } - } - } else if (components.size() == 2 && components[0].begins_with("navigation_layer_") && components[0].trim_prefix("navigation_layer_").is_valid_int()) { - // Navigation layers. - int layer_index = components[0].trim_prefix("navigation_layer_").to_int(); - ERR_FAIL_COND_V(layer_index < 0, false); - if (components[1] == "polygon") { - Ref polygon = p_value; - if (!polygon.is_valid()) { - return false; - } + emit_signal("changed"); +} - if (layer_index >= navigation.size()) { - if (tile_set) { - return false; - } else { - navigation.resize(layer_index + 1); - } - } - set_navigation_polygon(layer_index, polygon); - return true; - } - } else if (components.size() == 2 && components[0] == "terrains_peering_bit") { - // Terrains. - if (components[1] == "right_side") { - set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_RIGHT_SIDE, p_value); - } else if (components[1] == "right_corner") { - set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_RIGHT_CORNER, p_value); - } else if (components[1] == "bottom_right_side") { - set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE, p_value); - } else if (components[1] == "bottom_right_corner") { - set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER, p_value); - } else if (components[1] == "bottom_side") { - set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_SIDE, p_value); - } else if (components[1] == "bottom_corner") { - set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_CORNER, p_value); - } else if (components[1] == "bottom_left_side") { - set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE, p_value); - } else if (components[1] == "bottom_left_corner") { - set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER, p_value); - } else if (components[1] == "left_side") { - set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_LEFT_SIDE, p_value); - } else if (components[1] == "left_corner") { - set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_LEFT_CORNER, p_value); - } else if (components[1] == "top_left_side") { - set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE, p_value); - } else if (components[1] == "top_left_corner") { - set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER, p_value); - } else if (components[1] == "top_side") { - set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_SIDE, p_value); - } else if (components[1] == "top_corner") { - set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_CORNER, p_value); - } else if (components[1] == "top_right_side") { - set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE, p_value); - } else if (components[1] == "top_right_corner") { - set_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER, p_value); - } else { - return false; - } - return true; - } else if (components.size() == 1 && components[0].begins_with("custom_data_") && components[0].trim_prefix("custom_data_").is_valid_int()) { - // Custom data layers. - int layer_index = components[0].trim_prefix("custom_data_").to_int(); - ERR_FAIL_COND_V(layer_index < 0, false); +bool TileSetScenesCollectionSource::get_scene_tile_display_placeholder(int p_id) const { + ERR_FAIL_COND_V(!scenes.has(p_id), false); + return scenes[p_id].display_placeholder; +} - if (layer_index >= custom_data.size()) { - if (tile_set) { - return false; - } else { - custom_data.resize(layer_index + 1); - } - } - set_custom_data_by_layer_id(layer_index, p_value); +void TileSetScenesCollectionSource::remove_scene_tile(int p_id) { + ERR_FAIL_COND(!scenes.has(p_id)); - return true; - } + scenes.erase(p_id); + scenes_ids.erase(p_id); + emit_signal("changed"); +} - return false; +int TileSetScenesCollectionSource::get_next_scene_tile_id() const { + return next_scene_id; } -bool TileData::_get(const StringName &p_name, Variant &r_ret) const { +bool TileSetScenesCollectionSource::_set(const StringName &p_name, const Variant &p_value) { Vector components = String(p_name).split("/", true, 2); - if (tile_set) { - if (components.size() == 2 && components[0].begins_with("occlusion_layer") && components[0].trim_prefix("occlusion_layer_").is_valid_int()) { - // Occlusion layers. - int layer_index = components[0].trim_prefix("occlusion_layer_").to_int(); - ERR_FAIL_COND_V(layer_index < 0, false); - if (layer_index >= occluders.size()) { - return false; - } - if (components[1] == "polygon") { - r_ret = get_occluder(layer_index); - return true; - } - } else if (components.size() >= 2 && components[0].begins_with("physics_layer_") && components[0].trim_prefix("physics_layer_").is_valid_int()) { - // Physics layers. - int layer_index = components[0].trim_prefix("physics_layer_").to_int(); - ERR_FAIL_COND_V(layer_index < 0, false); - if (layer_index >= physics.size()) { - return false; - } - if (components.size() == 2 && components[1] == "shapes_count") { - r_ret = get_collision_shapes_count(layer_index); - return true; - } else if (components.size() == 3 && components[1].begins_with("shape_") && components[1].trim_prefix("shape_").is_valid_int()) { - int shape_index = components[1].trim_prefix("shape_").to_int(); - ERR_FAIL_COND_V(shape_index < 0, false); - if (shape_index >= physics[layer_index].shapes.size()) { - return false; - } - if (components[2] == "shape") { - r_ret = get_collision_shape_shape(layer_index, shape_index); - return true; - } else if (components[2] == "one_way") { - r_ret = is_collision_shape_one_way(layer_index, shape_index); - return true; - } else if (components[2] == "one_way_margin") { - r_ret = get_collision_shape_one_way_margin(layer_index, shape_index); - return true; - } - } - } else if (components.size() == 2 && components[0] == "terrains_peering_bit") { - // Terrains. - if (components[1] == "right_side") { - r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_RIGHT_SIDE]; - } else if (components[1] == "right_corner") { - r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_RIGHT_CORNER]; - } else if (components[1] == "bottom_right_side") { - r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE]; - } else if (components[1] == "bottom_right_corner") { - r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER]; - } else if (components[1] == "bottom_side") { - r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_BOTTOM_SIDE]; - } else if (components[1] == "bottom_corner") { - r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_BOTTOM_CORNER]; - } else if (components[1] == "bottom_left_side") { - r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE]; - } else if (components[1] == "bottom_left_corner") { - r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER]; - } else if (components[1] == "left_side") { - r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_LEFT_SIDE]; - } else if (components[1] == "left_corner") { - r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_LEFT_CORNER]; - } else if (components[1] == "top_left_side") { - r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE]; - } else if (components[1] == "top_left_corner") { - r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER]; - } else if (components[1] == "top_side") { - r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_TOP_SIDE]; - } else if (components[1] == "top_corner") { - r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_TOP_CORNER]; - } else if (components[1] == "top_right_side") { - r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE]; - } else if (components[1] == "top_right_corner") { - r_ret = terrain_peering_bits[TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER]; + if (components.size() >= 2 && components[0] == "scenes" && components[1].is_valid_int()) { + int scene_id = components[1].to_int(); + if (components.size() >= 3 && components[2] == "scene") { + if (has_scene_tile_id(scene_id)) { + set_scene_tile_scene(scene_id, p_value); } else { - return false; - } - return true; - } else if (components.size() == 2 && components[0].begins_with("navigation_layer_") && components[0].trim_prefix("navigation_layer_").is_valid_int()) { - // Occlusion layers. - int layer_index = components[0].trim_prefix("navigation_layer_").to_int(); - ERR_FAIL_COND_V(layer_index < 0, false); - if (layer_index >= navigation.size()) { - return false; - } - if (components[1] == "polygon") { - r_ret = get_navigation_polygon(layer_index); - return true; + create_scene_tile(p_value, scene_id); } - } else if (components.size() == 1 && components[0].begins_with("custom_data_") && components[0].trim_prefix("custom_data_").is_valid_int()) { - // Custom data layers. - int layer_index = components[0].trim_prefix("custom_data_").to_int(); - ERR_FAIL_COND_V(layer_index < 0, false); - if (layer_index >= custom_data.size()) { - return false; + return true; + } else if (components.size() >= 3 && components[2] == "display_placeholder") { + if (!has_scene_tile_id(scene_id)) { + create_scene_tile(p_value, scene_id); } - r_ret = get_custom_data_by_layer_id(layer_index); + return true; } } @@ -2777,1062 +3054,734 @@ bool TileData::_get(const StringName &p_name, Variant &r_ret) const { return false; } -void TileData::_get_property_list(List *p_list) const { - PropertyInfo property_info; - // Add the groups manually. - if (tile_set) { - // Occlusion layers. - p_list->push_back(PropertyInfo(Variant::NIL, "Rendering", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP)); - for (int i = 0; i < occluders.size(); i++) { - // occlusion_layer_%d/polygon - property_info = PropertyInfo(Variant::OBJECT, vformat("occlusion_layer_%d/polygon", i), PROPERTY_HINT_RESOURCE_TYPE, "OccluderPolygon2D", PROPERTY_USAGE_DEFAULT); - if (!occluders[i].is_valid()) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; - } - p_list->push_back(property_info); - } - - // Physics layers. - p_list->push_back(PropertyInfo(Variant::NIL, "Physics", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP)); - for (int i = 0; i < physics.size(); i++) { - p_list->push_back(PropertyInfo(Variant::INT, vformat("physics_layer_%d/shapes_count", i), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR)); - - for (int j = 0; j < physics[i].shapes.size(); j++) { - // physics_layer_%d/shapes_count - property_info = PropertyInfo(Variant::OBJECT, vformat("physics_layer_%d/shape_%d/shape", i, j), PROPERTY_HINT_RESOURCE_TYPE, "Shape2D", PROPERTY_USAGE_DEFAULT); - if (!physics[i].shapes[j].shape.is_valid()) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; - } - p_list->push_back(property_info); - - // physics_layer_%d/shape_%d/one_way - property_info = PropertyInfo(Variant::BOOL, vformat("physics_layer_%d/shape_%d/one_way", i, j)); - if (physics[i].shapes[j].one_way == false) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; - } - p_list->push_back(property_info); +bool TileSetScenesCollectionSource::_get(const StringName &p_name, Variant &r_ret) const { + Vector components = String(p_name).split("/", true, 2); - // physics_layer_%d/shape_%d/one_way_margin - property_info = PropertyInfo(Variant::FLOAT, vformat("physics_layer_%d/shape_%d/one_way_margin", i, j)); - if (physics[i].shapes[j].one_way_margin == 1.0) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; - } - p_list->push_back(property_info); - } + if (components.size() >= 2 && components[0] == "scenes" && components[1].is_valid_int() && scenes.has(components[1].to_int())) { + if (components.size() >= 3 && components[2] == "scene") { + r_ret = scenes[components[1].to_int()].scene; + return true; + } else if (components.size() >= 3 && components[2] == "display_placeholder") { + r_ret = scenes[components[1].to_int()].scene; + return true; } + } - // Terrain data - if (terrain_set >= 0) { - p_list->push_back(PropertyInfo(Variant::NIL, "Terrains", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP)); - if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_RIGHT_SIDE)) { - property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/right_side"); - if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_RIGHT_SIDE) == -1) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; - } - p_list->push_back(property_info); - } - if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_RIGHT_CORNER)) { - property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/right_corner"); - if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_RIGHT_CORNER) == -1) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; - } - p_list->push_back(property_info); - } - if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)) { - property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/bottom_right_side"); - if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE) == -1) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; - } - p_list->push_back(property_info); - } - if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER)) { - property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/bottom_right_corner"); - if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER) == -1) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; - } - p_list->push_back(property_info); - } - if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)) { - property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/bottom_side"); - if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_SIDE) == -1) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; - } - p_list->push_back(property_info); - } - if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_CORNER)) { - property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/bottom_corner"); - if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_CORNER) == -1) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; - } - p_list->push_back(property_info); - } - if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)) { - property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/bottom_left_side"); - if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE) == -1) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; - } - p_list->push_back(property_info); - } - if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER)) { - property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/bottom_left_corner"); - if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER) == -1) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; - } - p_list->push_back(property_info); - } - if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_LEFT_SIDE)) { - property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/left_side"); - if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_LEFT_SIDE) == -1) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; - } - p_list->push_back(property_info); - } - if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_LEFT_CORNER)) { - property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/left_corner"); - if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_LEFT_CORNER) == -1) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; - } - p_list->push_back(property_info); - } - if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE)) { - property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/top_left_side"); - if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE) == -1) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; - } - p_list->push_back(property_info); - } - if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER)) { - property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/top_left_corner"); - if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER) == -1) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; - } - p_list->push_back(property_info); - } - if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_SIDE)) { - property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/top_side"); - if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_SIDE) == -1) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; - } - p_list->push_back(property_info); - } - if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_CORNER)) { - property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/top_corner"); - if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_CORNER) == -1) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; - } - p_list->push_back(property_info); - } - if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE)) { - property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/top_right_side"); - if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE) == -1) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; - } - p_list->push_back(property_info); - } - if (is_valid_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER)) { - property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/top_right_corner"); - if (get_peering_bit_terrain(TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER) == -1) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; - } - p_list->push_back(property_info); - } - } + return false; +} - // Navigation layers. - p_list->push_back(PropertyInfo(Variant::NIL, "Navigation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP)); - for (int i = 0; i < navigation.size(); i++) { - property_info = PropertyInfo(Variant::OBJECT, vformat("navigation_layer_%d/polygon", i), PROPERTY_HINT_RESOURCE_TYPE, "NavigationPolygon", PROPERTY_USAGE_DEFAULT); - if (!navigation[i].is_valid()) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; - } - p_list->push_back(property_info); - } +void TileSetScenesCollectionSource::_get_property_list(List *p_list) const { + for (int i = 0; i < scenes_ids.size(); i++) { + p_list->push_back(PropertyInfo(Variant::OBJECT, vformat("scenes/%d/scene", scenes_ids[i]), PROPERTY_HINT_RESOURCE_TYPE, "TileSetScenesCollectionSource")); - // Custom data layers. - p_list->push_back(PropertyInfo(Variant::NIL, "Custom data", PROPERTY_HINT_NONE, "custom_data_", PROPERTY_USAGE_GROUP)); - for (int i = 0; i < custom_data.size(); i++) { - Variant default_val; - Callable::CallError error; - Variant::construct(custom_data[i].get_type(), default_val, nullptr, 0, error); - property_info = PropertyInfo(tile_set->get_custom_data_type(i), vformat("custom_data_%d", i), PROPERTY_HINT_NONE, String(), PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT); - if (custom_data[i] == default_val) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; - } - p_list->push_back(property_info); + PropertyInfo property_info = PropertyInfo(Variant::BOOL, vformat("scenes/%d/display_placeholder", scenes_ids[i])); + if (scenes[scenes_ids[i]].display_placeholder == false) { + property_info.usage ^= PROPERTY_USAGE_STORAGE; } + p_list->push_back(property_info); } } -void TileData::_bind_methods() { - // Rendering. - ClassDB::bind_method(D_METHOD("set_flip_h", "flip_h"), &TileData::set_flip_h); - ClassDB::bind_method(D_METHOD("get_flip_h"), &TileData::get_flip_h); - ClassDB::bind_method(D_METHOD("set_flip_v", "flip_v"), &TileData::set_flip_v); - ClassDB::bind_method(D_METHOD("get_flip_v"), &TileData::get_flip_v); - ClassDB::bind_method(D_METHOD("set_transpose", "transpose"), &TileData::set_transpose); - ClassDB::bind_method(D_METHOD("get_transpose"), &TileData::get_transpose); - ClassDB::bind_method(D_METHOD("tile_set_material", "material"), &TileData::tile_set_material); - ClassDB::bind_method(D_METHOD("tile_get_material"), &TileData::tile_get_material); - ClassDB::bind_method(D_METHOD("set_texture_offset", "texture_offset"), &TileData::set_texture_offset); - ClassDB::bind_method(D_METHOD("get_texture_offset"), &TileData::get_texture_offset); - ClassDB::bind_method(D_METHOD("set_modulate", "modulate"), &TileData::set_modulate); - ClassDB::bind_method(D_METHOD("get_modulate"), &TileData::get_modulate); - ClassDB::bind_method(D_METHOD("set_z_index", "z_index"), &TileData::set_z_index); - ClassDB::bind_method(D_METHOD("get_z_index"), &TileData::get_z_index); - ClassDB::bind_method(D_METHOD("set_y_sort_origin", "y_sort_origin"), &TileData::set_y_sort_origin); - ClassDB::bind_method(D_METHOD("get_y_sort_origin"), &TileData::get_y_sort_origin); +void TileSetScenesCollectionSource::_bind_methods() { + // Base tiles + ClassDB::bind_method(D_METHOD("get_tiles_count"), &TileSetScenesCollectionSource::get_tiles_count); + ClassDB::bind_method(D_METHOD("get_tile_id", "index"), &TileSetScenesCollectionSource::get_tile_id); + ClassDB::bind_method(D_METHOD("has_tile", "atlas_coords"), &TileSetScenesCollectionSource::has_tile); - ClassDB::bind_method(D_METHOD("set_occluder", "layer_id", "occluder_polygon"), &TileData::set_occluder); - ClassDB::bind_method(D_METHOD("get_occluder", "layer_id"), &TileData::get_occluder); + // Alternative tiles + ClassDB::bind_method(D_METHOD("get_alternative_tiles_count", "atlas_coords"), &TileSetScenesCollectionSource::get_alternative_tiles_count); + ClassDB::bind_method(D_METHOD("get_alternative_tile_id", "atlas_coords", "index"), &TileSetScenesCollectionSource::get_alternative_tile_id); + ClassDB::bind_method(D_METHOD("has_alternative_tile", "atlas_coords", "alternative_tile"), &TileSetScenesCollectionSource::has_alternative_tile); - // Physics. - ClassDB::bind_method(D_METHOD("get_collision_shapes_count", "layer_id"), &TileData::get_collision_shapes_count); - ClassDB::bind_method(D_METHOD("set_collision_shapes_count", "layer_id", "shapes_count"), &TileData::set_collision_shapes_count); - ClassDB::bind_method(D_METHOD("add_collision_shape", "layer_id"), &TileData::add_collision_shape); - ClassDB::bind_method(D_METHOD("remove_collision_shape", "layer_id", "shape_index"), &TileData::remove_collision_shape); - ClassDB::bind_method(D_METHOD("set_collision_shape_shape", "layer_id", "shape_index", "shape"), &TileData::set_collision_shape_shape); - ClassDB::bind_method(D_METHOD("get_collision_shape_shape", "layer_id", "shape_index"), &TileData::get_collision_shape_shape); - ClassDB::bind_method(D_METHOD("set_collision_shape_one_way", "layer_id", "shape_index", "one_way"), &TileData::set_collision_shape_one_way); - ClassDB::bind_method(D_METHOD("is_collision_shape_one_way", "layer_id", "shape_index"), &TileData::is_collision_shape_one_way); - ClassDB::bind_method(D_METHOD("set_collision_shape_one_way_margin", "layer_id", "shape_index", "one_way_margin"), &TileData::set_collision_shape_one_way_margin); - ClassDB::bind_method(D_METHOD("get_collision_shape_one_way_margin", "layer_id", "shape_index"), &TileData::get_collision_shape_one_way_margin); + ClassDB::bind_method(D_METHOD("get_scene_tiles_count"), &TileSetScenesCollectionSource::get_scene_tiles_count); + ClassDB::bind_method(D_METHOD("get_scene_tile_id", "index"), &TileSetScenesCollectionSource::get_scene_tile_id); + ClassDB::bind_method(D_METHOD("has_scene_tile_id", "id"), &TileSetScenesCollectionSource::has_scene_tile_id); + ClassDB::bind_method(D_METHOD("create_scene_tile", "packed_scene", "id_override"), &TileSetScenesCollectionSource::create_scene_tile, DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("set_scene_tile_id", "id", "new_id"), &TileSetScenesCollectionSource::set_scene_tile_id); + ClassDB::bind_method(D_METHOD("set_scene_tile_scene", "id", "packed_scene"), &TileSetScenesCollectionSource::set_scene_tile_scene); + ClassDB::bind_method(D_METHOD("get_scene_tile_scene", "id"), &TileSetScenesCollectionSource::get_scene_tile_scene); + ClassDB::bind_method(D_METHOD("set_scene_tile_display_placeholder", "id", "display_placeholder"), &TileSetScenesCollectionSource::set_scene_tile_display_placeholder); + ClassDB::bind_method(D_METHOD("get_scene_tile_display_placeholder", "id"), &TileSetScenesCollectionSource::get_scene_tile_display_placeholder); + ClassDB::bind_method(D_METHOD("remove_scene_tile", "id"), &TileSetScenesCollectionSource::remove_scene_tile); + ClassDB::bind_method(D_METHOD("get_next_scene_tile_id"), &TileSetScenesCollectionSource::get_next_scene_tile_id); +} - // Terrain - ClassDB::bind_method(D_METHOD("set_terrain_set", "terrain_set"), &TileData::set_terrain_set); - ClassDB::bind_method(D_METHOD("get_terrain_set"), &TileData::get_terrain_set); - ClassDB::bind_method(D_METHOD("set_peering_bit_terrain", "peering_bit", "terrain"), &TileData::set_peering_bit_terrain); - ClassDB::bind_method(D_METHOD("get_peering_bit_terrain", "peering_bit"), &TileData::get_peering_bit_terrain); +/////////////////////////////// TileData ////////////////////////////////////// - // Navigation - ClassDB::bind_method(D_METHOD("set_navigation_polygon", "layer_id", "navigation_polygon"), &TileData::set_navigation_polygon); - ClassDB::bind_method(D_METHOD("get_navigation_polygon", "layer_id"), &TileData::get_navigation_polygon); +void TileData::set_tile_set(const TileSet *p_tile_set) { + tile_set = p_tile_set; + notify_tile_data_properties_should_change(); +} - // Misc. - ClassDB::bind_method(D_METHOD("set_probability", "probability"), &TileData::set_probability); - ClassDB::bind_method(D_METHOD("get_probability"), &TileData::get_probability); +void TileData::notify_tile_data_properties_should_change() { + if (!tile_set) { + return; + } - // Custom data. - ClassDB::bind_method(D_METHOD("set_custom_data", "layer_name", "value"), &TileData::set_custom_data); - ClassDB::bind_method(D_METHOD("get_custom_data", "layer_name"), &TileData::get_custom_data); - ClassDB::bind_method(D_METHOD("set_custom_data_by_layer_id", "layer_id", "value"), &TileData::set_custom_data_by_layer_id); - ClassDB::bind_method(D_METHOD("get_custom_data_by_layer_id", "layer_id"), &TileData::get_custom_data_by_layer_id); + occluders.resize(tile_set->get_occlusion_layers_count()); + physics.resize(tile_set->get_physics_layers_count()); + for (int bit_index = 0; bit_index < 16; bit_index++) { + if (terrain_set < 0 || terrain_peering_bits[bit_index] >= tile_set->get_terrains_count(terrain_set)) { + terrain_peering_bits[bit_index] = -1; + } + } + navigation.resize(tile_set->get_navigation_layers_count()); - ADD_GROUP("Rendering", ""); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_h"), "set_flip_h", "get_flip_h"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_v"), "set_flip_v", "get_flip_v"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "transpose"), "set_transpose", "get_transpose"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "texture_offset"), "set_texture_offset", "get_texture_offset"); - ADD_PROPERTY(PropertyInfo(Variant::COLOR, "modulate"), "set_modulate", "get_modulate"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "z_index"), "set_z_index", "get_z_index"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "y_sort_origin"), "set_y_sort_origin", "get_y_sort_origin"); + // Convert custom data to the new type. + custom_data.resize(tile_set->get_custom_data_layers_count()); + for (int i = 0; i < custom_data.size(); i++) { + if (custom_data[i].get_type() != tile_set->get_custom_data_type(i)) { + Variant new_val; + Callable::CallError error; + if (Variant::can_convert(custom_data[i].get_type(), tile_set->get_custom_data_type(i))) { + const Variant *args[] = { &custom_data[i] }; + Variant::construct(tile_set->get_custom_data_type(i), new_val, args, 1, error); + } else { + Variant::construct(tile_set->get_custom_data_type(i), new_val, nullptr, 0, error); + } + custom_data.write[i] = new_val; + } + } - ADD_GROUP("Terrains", ""); - ADD_PROPERTY(PropertyInfo(Variant::INT, "terrain_set"), "set_terrain_set", "get_terrain_set"); + notify_property_list_changed(); + emit_signal("changed"); +} + +void TileData::reset_state() { + occluders.clear(); + physics.clear(); + navigation.clear(); + custom_data.clear(); +} - ADD_GROUP("Miscellaneous", ""); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "probability"), "set_probability", "get_probability"); +void TileData::set_allow_transform(bool p_allow_transform) { + allow_transform = p_allow_transform; +} - ADD_SIGNAL(MethodInfo("changed")); +bool TileData::is_allowing_transform() const { + return allow_transform; } -/////////////////////////////// TileSetPluginAtlasTerrain ////////////////////////////////////// +// Rendering +void TileData::set_flip_h(bool p_flip_h) { + ERR_FAIL_COND_MSG(!allow_transform && p_flip_h, "Transform is only allowed for alternative tiles (with its alternative_id != 0)"); + flip_h = p_flip_h; + emit_signal("changed"); +} +bool TileData::get_flip_h() const { + return flip_h; +} -// --- PLUGINS --- -void TileSetPluginAtlasTerrain::_draw_square_corner_or_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit) { - Rect2 bit_rect; - bit_rect.size = Vector2(p_size) / 3; - switch (p_bit) { - case TileSet::CELL_NEIGHBOR_RIGHT_SIDE: - bit_rect.position = Vector2(1, -1); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: - bit_rect.position = Vector2(1, 1); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE: - bit_rect.position = Vector2(-1, 1); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER: - bit_rect.position = Vector2(-3, 1); - break; - case TileSet::CELL_NEIGHBOR_LEFT_SIDE: - bit_rect.position = Vector2(-3, -1); - break; - case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER: - bit_rect.position = Vector2(-3, -3); - break; - case TileSet::CELL_NEIGHBOR_TOP_SIDE: - bit_rect.position = Vector2(-1, -3); - break; - case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER: - bit_rect.position = Vector2(1, -3); - break; - default: - break; - } - bit_rect.position *= Vector2(p_size) / 6.0; - p_canvas_item->draw_rect(bit_rect, p_color); +void TileData::set_flip_v(bool p_flip_v) { + ERR_FAIL_COND_MSG(!allow_transform && p_flip_v, "Transform is only allowed for alternative tiles (with its alternative_id != 0)"); + flip_v = p_flip_v; + emit_signal("changed"); } -void TileSetPluginAtlasTerrain::_draw_square_corner_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit) { - PackedColorArray color_array; - color_array.push_back(p_color); +bool TileData::get_flip_v() const { + return flip_v; +} - Vector2 unit = Vector2(p_size) / 6.0; - PackedVector2Array polygon; - switch (p_bit) { - case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: - polygon.push_back(Vector2(0, 3) * unit); - polygon.push_back(Vector2(3, 3) * unit); - polygon.push_back(Vector2(3, 0) * unit); - polygon.push_back(Vector2(1, 0) * unit); - polygon.push_back(Vector2(1, 1) * unit); - polygon.push_back(Vector2(0, 1) * unit); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER: - polygon.push_back(Vector2(0, 3) * unit); - polygon.push_back(Vector2(-3, 3) * unit); - polygon.push_back(Vector2(-3, 0) * unit); - polygon.push_back(Vector2(-1, 0) * unit); - polygon.push_back(Vector2(-1, 1) * unit); - polygon.push_back(Vector2(0, 1) * unit); - break; - case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER: - polygon.push_back(Vector2(0, -3) * unit); - polygon.push_back(Vector2(-3, -3) * unit); - polygon.push_back(Vector2(-3, 0) * unit); - polygon.push_back(Vector2(-1, 0) * unit); - polygon.push_back(Vector2(-1, -1) * unit); - polygon.push_back(Vector2(0, -1) * unit); - break; - case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER: - polygon.push_back(Vector2(0, -3) * unit); - polygon.push_back(Vector2(3, -3) * unit); - polygon.push_back(Vector2(3, 0) * unit); - polygon.push_back(Vector2(1, 0) * unit); - polygon.push_back(Vector2(1, -1) * unit); - polygon.push_back(Vector2(0, -1) * unit); - break; - default: - break; - } - if (!polygon.is_empty()) { - p_canvas_item->draw_polygon(polygon, color_array); - } +void TileData::set_transpose(bool p_transpose) { + ERR_FAIL_COND_MSG(!allow_transform && p_transpose, "Transform is only allowed for alternative tiles (with its alternative_id != 0)"); + transpose = p_transpose; + emit_signal("changed"); +} +bool TileData::get_transpose() const { + return transpose; } -void TileSetPluginAtlasTerrain::_draw_square_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit) { - PackedColorArray color_array; - color_array.push_back(p_color); +void TileData::set_texture_offset(Vector2i p_texture_offset) { + tex_offset = p_texture_offset; + emit_signal("changed"); +} - Vector2 unit = Vector2(p_size) / 6.0; - PackedVector2Array polygon; - switch (p_bit) { - case TileSet::CELL_NEIGHBOR_RIGHT_SIDE: - polygon.push_back(Vector2(1, -1) * unit); - polygon.push_back(Vector2(3, -3) * unit); - polygon.push_back(Vector2(3, 3) * unit); - polygon.push_back(Vector2(1, 1) * unit); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE: - polygon.push_back(Vector2(-1, 1) * unit); - polygon.push_back(Vector2(-3, 3) * unit); - polygon.push_back(Vector2(3, 3) * unit); - polygon.push_back(Vector2(1, 1) * unit); - break; - case TileSet::CELL_NEIGHBOR_LEFT_SIDE: - polygon.push_back(Vector2(-1, -1) * unit); - polygon.push_back(Vector2(-3, -3) * unit); - polygon.push_back(Vector2(-3, 3) * unit); - polygon.push_back(Vector2(-1, 1) * unit); - break; - case TileSet::CELL_NEIGHBOR_TOP_SIDE: - polygon.push_back(Vector2(-1, -1) * unit); - polygon.push_back(Vector2(-3, -3) * unit); - polygon.push_back(Vector2(3, -3) * unit); - polygon.push_back(Vector2(1, -1) * unit); - break; - default: - break; - } - if (!polygon.is_empty()) { - p_canvas_item->draw_polygon(polygon, color_array); - } +Vector2i TileData::get_texture_offset() const { + return tex_offset; } -void TileSetPluginAtlasTerrain::_draw_isometric_corner_or_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit) { - PackedColorArray color_array; - color_array.push_back(p_color); +void TileData::tile_set_material(Ref p_material) { + material = p_material; + emit_signal("changed"); +} +Ref TileData::tile_get_material() const { + return material; +} - Vector2 unit = Vector2(p_size) / 6.0; - PackedVector2Array polygon; - switch (p_bit) { - case TileSet::CELL_NEIGHBOR_RIGHT_CORNER: - polygon.push_back(Vector2(1, 0) * unit); - polygon.push_back(Vector2(2, -1) * unit); - polygon.push_back(Vector2(3, 0) * unit); - polygon.push_back(Vector2(2, 1) * unit); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE: - polygon.push_back(Vector2(0, 1) * unit); - polygon.push_back(Vector2(1, 2) * unit); - polygon.push_back(Vector2(2, 1) * unit); - polygon.push_back(Vector2(1, 0) * unit); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER: - polygon.push_back(Vector2(0, 1) * unit); - polygon.push_back(Vector2(-1, 2) * unit); - polygon.push_back(Vector2(0, 3) * unit); - polygon.push_back(Vector2(1, 2) * unit); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE: - polygon.push_back(Vector2(0, 1) * unit); - polygon.push_back(Vector2(-1, 2) * unit); - polygon.push_back(Vector2(-2, 1) * unit); - polygon.push_back(Vector2(-1, 0) * unit); - break; - case TileSet::CELL_NEIGHBOR_LEFT_CORNER: - polygon.push_back(Vector2(-1, 0) * unit); - polygon.push_back(Vector2(-2, -1) * unit); - polygon.push_back(Vector2(-3, 0) * unit); - polygon.push_back(Vector2(-2, 1) * unit); - break; - case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE: - polygon.push_back(Vector2(0, -1) * unit); - polygon.push_back(Vector2(-1, -2) * unit); - polygon.push_back(Vector2(-2, -1) * unit); - polygon.push_back(Vector2(-1, 0) * unit); - break; - case TileSet::CELL_NEIGHBOR_TOP_CORNER: - polygon.push_back(Vector2(0, -1) * unit); - polygon.push_back(Vector2(-1, -2) * unit); - polygon.push_back(Vector2(0, -3) * unit); - polygon.push_back(Vector2(1, -2) * unit); - break; - case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE: - polygon.push_back(Vector2(0, -1) * unit); - polygon.push_back(Vector2(1, -2) * unit); - polygon.push_back(Vector2(2, -1) * unit); - polygon.push_back(Vector2(1, 0) * unit); - break; - default: - break; - } - if (!polygon.is_empty()) { - p_canvas_item->draw_polygon(polygon, color_array); - } +void TileData::set_modulate(Color p_modulate) { + modulate = p_modulate; + emit_signal("changed"); +} +Color TileData::get_modulate() const { + return modulate; } -void TileSetPluginAtlasTerrain::_draw_isometric_corner_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit) { - PackedColorArray color_array; - color_array.push_back(p_color); +void TileData::set_z_index(int p_z_index) { + z_index = p_z_index; + emit_signal("changed"); +} +int TileData::get_z_index() const { + return z_index; +} - Vector2 unit = Vector2(p_size) / 6.0; - PackedVector2Array polygon; - switch (p_bit) { - case TileSet::CELL_NEIGHBOR_RIGHT_CORNER: - polygon.push_back(Vector2(0.5, -0.5) * unit); - polygon.push_back(Vector2(1.5, -1.5) * unit); - polygon.push_back(Vector2(3, 0) * unit); - polygon.push_back(Vector2(1.5, 1.5) * unit); - polygon.push_back(Vector2(0.5, 0.5) * unit); - polygon.push_back(Vector2(1, 0) * unit); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER: - polygon.push_back(Vector2(-0.5, 0.5) * unit); - polygon.push_back(Vector2(-1.5, 1.5) * unit); - polygon.push_back(Vector2(0, 3) * unit); - polygon.push_back(Vector2(1.5, 1.5) * unit); - polygon.push_back(Vector2(0.5, 0.5) * unit); - polygon.push_back(Vector2(0, 1) * unit); - break; - case TileSet::CELL_NEIGHBOR_LEFT_CORNER: - polygon.push_back(Vector2(-0.5, -0.5) * unit); - polygon.push_back(Vector2(-1.5, -1.5) * unit); - polygon.push_back(Vector2(-3, 0) * unit); - polygon.push_back(Vector2(-1.5, 1.5) * unit); - polygon.push_back(Vector2(-0.5, 0.5) * unit); - polygon.push_back(Vector2(-1, 0) * unit); - break; - case TileSet::CELL_NEIGHBOR_TOP_CORNER: - polygon.push_back(Vector2(-0.5, -0.5) * unit); - polygon.push_back(Vector2(-1.5, -1.5) * unit); - polygon.push_back(Vector2(0, -3) * unit); - polygon.push_back(Vector2(1.5, -1.5) * unit); - polygon.push_back(Vector2(0.5, -0.5) * unit); - polygon.push_back(Vector2(0, -1) * unit); - break; - default: - break; - } - if (!polygon.is_empty()) { - p_canvas_item->draw_polygon(polygon, color_array); - } +void TileData::set_y_sort_origin(int p_y_sort_origin) { + y_sort_origin = p_y_sort_origin; + emit_signal("changed"); +} +int TileData::get_y_sort_origin() const { + return y_sort_origin; } -void TileSetPluginAtlasTerrain::_draw_isometric_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit) { - PackedColorArray color_array; - color_array.push_back(p_color); +void TileData::set_occluder(int p_layer_id, Ref p_occluder_polygon) { + ERR_FAIL_INDEX(p_layer_id, occluders.size()); + occluders.write[p_layer_id] = p_occluder_polygon; + emit_signal("changed"); +} - Vector2 unit = Vector2(p_size) / 6.0; - PackedVector2Array polygon; - switch (p_bit) { - case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE: - polygon.push_back(Vector2(1, 0) * unit); - polygon.push_back(Vector2(3, 0) * unit); - polygon.push_back(Vector2(0, 3) * unit); - polygon.push_back(Vector2(0, 1) * unit); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE: - polygon.push_back(Vector2(-1, 0) * unit); - polygon.push_back(Vector2(-3, 0) * unit); - polygon.push_back(Vector2(0, 3) * unit); - polygon.push_back(Vector2(0, 1) * unit); - break; - case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE: - polygon.push_back(Vector2(-1, 0) * unit); - polygon.push_back(Vector2(-3, 0) * unit); - polygon.push_back(Vector2(0, -3) * unit); - polygon.push_back(Vector2(0, -1) * unit); - break; - case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE: - polygon.push_back(Vector2(1, 0) * unit); - polygon.push_back(Vector2(3, 0) * unit); - polygon.push_back(Vector2(0, -3) * unit); - polygon.push_back(Vector2(0, -1) * unit); - break; - default: - break; - } - if (!polygon.is_empty()) { - p_canvas_item->draw_polygon(polygon, color_array); - } +Ref TileData::get_occluder(int p_layer_id) const { + ERR_FAIL_INDEX_V(p_layer_id, occluders.size(), Ref()); + return occluders[p_layer_id]; } -void TileSetPluginAtlasTerrain::_draw_half_offset_corner_or_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis) { - PackedColorArray color_array; - color_array.push_back(p_color); +// Physics +int TileData::get_collision_polygons_count(int p_layer_id) const { + ERR_FAIL_INDEX_V(p_layer_id, physics.size(), 0); + return physics[p_layer_id].polygons.size(); +} - PackedVector2Array point_list; - point_list.push_back(Vector2(3, (3.0 * (1.0 - p_overlap * 2.0)) / 2.0)); - point_list.push_back(Vector2(3, 3.0 * (1.0 - p_overlap * 2.0))); - point_list.push_back(Vector2(2, 3.0 * (1.0 - (p_overlap * 2.0) * 2.0 / 3.0))); - point_list.push_back(Vector2(1, 3.0 - p_overlap * 2.0)); - point_list.push_back(Vector2(0, 3)); - point_list.push_back(Vector2(-1, 3.0 - p_overlap * 2.0)); - point_list.push_back(Vector2(-2, 3.0 * (1.0 - (p_overlap * 2.0) * 2.0 / 3.0))); - point_list.push_back(Vector2(-3, 3.0 * (1.0 - p_overlap * 2.0))); - point_list.push_back(Vector2(-3, (3.0 * (1.0 - p_overlap * 2.0)) / 2.0)); - point_list.push_back(Vector2(-3, -(3.0 * (1.0 - p_overlap * 2.0)) / 2.0)); - point_list.push_back(Vector2(-3, -3.0 * (1.0 - p_overlap * 2.0))); - point_list.push_back(Vector2(-2, -3.0 * (1.0 - (p_overlap * 2.0) * 2.0 / 3.0))); - point_list.push_back(Vector2(-1, -(3.0 - p_overlap * 2.0))); - point_list.push_back(Vector2(0, -3)); - point_list.push_back(Vector2(1, -(3.0 - p_overlap * 2.0))); - point_list.push_back(Vector2(2, -3.0 * (1.0 - (p_overlap * 2.0) * 2.0 / 3.0))); - point_list.push_back(Vector2(3, -3.0 * (1.0 - p_overlap * 2.0))); - point_list.push_back(Vector2(3, -(3.0 * (1.0 - p_overlap * 2.0)) / 2.0)); +void TileData::set_collision_polygons_count(int p_layer_id, int p_polygons_count) { + ERR_FAIL_INDEX(p_layer_id, physics.size()); + ERR_FAIL_COND(p_polygons_count < 0); + physics.write[p_layer_id].polygons.resize(p_polygons_count); + notify_property_list_changed(); + emit_signal("changed"); +} - Vector2 unit = Vector2(p_size) / 6.0; - for (int i = 0; i < point_list.size(); i++) { - point_list.write[i] = point_list[i] * unit; - } +void TileData::add_collision_polygon(int p_layer_id) { + ERR_FAIL_INDEX(p_layer_id, physics.size()); + physics.write[p_layer_id].polygons.push_back(PhysicsLayerTileData::PolygonShapeTileData()); + emit_signal("changed"); +} - PackedVector2Array polygon; - if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { - switch (p_bit) { - case TileSet::CELL_NEIGHBOR_RIGHT_SIDE: - polygon.push_back(point_list[17]); - polygon.push_back(point_list[0]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: - polygon.push_back(point_list[0]); - polygon.push_back(point_list[1]); - polygon.push_back(point_list[2]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE: - polygon.push_back(point_list[2]); - polygon.push_back(point_list[3]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER: - polygon.push_back(point_list[3]); - polygon.push_back(point_list[4]); - polygon.push_back(point_list[5]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE: - polygon.push_back(point_list[5]); - polygon.push_back(point_list[6]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER: - polygon.push_back(point_list[6]); - polygon.push_back(point_list[7]); - polygon.push_back(point_list[8]); - break; - case TileSet::CELL_NEIGHBOR_LEFT_SIDE: - polygon.push_back(point_list[8]); - polygon.push_back(point_list[9]); - break; - case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER: - polygon.push_back(point_list[9]); - polygon.push_back(point_list[10]); - polygon.push_back(point_list[11]); - break; - case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE: - polygon.push_back(point_list[11]); - polygon.push_back(point_list[12]); - break; - case TileSet::CELL_NEIGHBOR_TOP_CORNER: - polygon.push_back(point_list[12]); - polygon.push_back(point_list[13]); - polygon.push_back(point_list[14]); - break; - case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE: - polygon.push_back(point_list[14]); - polygon.push_back(point_list[15]); - break; - case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER: - polygon.push_back(point_list[15]); - polygon.push_back(point_list[16]); - polygon.push_back(point_list[17]); - break; - default: - break; - } +void TileData::remove_collision_polygon(int p_layer_id, int p_polygon_index) { + ERR_FAIL_INDEX(p_layer_id, physics.size()); + ERR_FAIL_INDEX(p_polygon_index, physics[p_layer_id].polygons.size()); + physics.write[p_layer_id].polygons.remove(p_polygon_index); + emit_signal("changed"); +} + +void TileData::set_collision_polygon_points(int p_layer_id, int p_polygon_index, Vector p_polygon) { + ERR_FAIL_INDEX(p_layer_id, physics.size()); + ERR_FAIL_INDEX(p_polygon_index, physics[p_layer_id].polygons.size()); + ERR_FAIL_COND_MSG(p_polygon.size() != 0 && p_polygon.size() < 3, "Invalid polygon. Needs either 0 or more than 3 points."); + + if (p_polygon.is_empty()) { + physics.write[p_layer_id].polygons.write[p_polygon_index].shapes.clear(); } else { - if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL) { - for (int i = 0; i < point_list.size(); i++) { - point_list.write[i] = Vector2(point_list[i].y, point_list[i].x); - } + // Decompose into convex shapes. + Vector> decomp = Geometry2D::decompose_polygon_in_convex(p_polygon); + ERR_FAIL_COND_MSG(decomp.is_empty(), "Could not decompose the polygon into convex shapes."); + + physics.write[p_layer_id].polygons.write[p_polygon_index].shapes.resize(decomp.size()); + for (int i = 0; i < decomp.size(); i++) { + Ref shape; + shape.instantiate(); + shape->set_points(decomp[i]); + physics.write[p_layer_id].polygons.write[p_polygon_index].shapes[i] = shape; } - switch (p_bit) { - case TileSet::CELL_NEIGHBOR_RIGHT_CORNER: - polygon.push_back(point_list[3]); - polygon.push_back(point_list[4]); - polygon.push_back(point_list[5]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE: - polygon.push_back(point_list[2]); - polygon.push_back(point_list[3]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: - polygon.push_back(point_list[0]); - polygon.push_back(point_list[1]); - polygon.push_back(point_list[2]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE: - polygon.push_back(point_list[17]); - polygon.push_back(point_list[0]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER: - polygon.push_back(point_list[15]); - polygon.push_back(point_list[16]); - polygon.push_back(point_list[17]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE: - polygon.push_back(point_list[14]); - polygon.push_back(point_list[15]); - break; - case TileSet::CELL_NEIGHBOR_LEFT_CORNER: - polygon.push_back(point_list[12]); - polygon.push_back(point_list[13]); - polygon.push_back(point_list[14]); - break; - case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE: - polygon.push_back(point_list[11]); - polygon.push_back(point_list[12]); - break; - case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER: - polygon.push_back(point_list[9]); - polygon.push_back(point_list[10]); - polygon.push_back(point_list[11]); - break; - case TileSet::CELL_NEIGHBOR_TOP_SIDE: - polygon.push_back(point_list[8]); - polygon.push_back(point_list[9]); - break; - case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER: - polygon.push_back(point_list[6]); - polygon.push_back(point_list[7]); - polygon.push_back(point_list[8]); - break; - case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE: - polygon.push_back(point_list[5]); - polygon.push_back(point_list[6]); - break; - default: - break; + } + physics.write[p_layer_id].polygons.write[p_polygon_index].polygon = p_polygon; + emit_signal("changed"); +} + +Vector TileData::get_collision_polygon_points(int p_layer_id, int p_polygon_index) const { + ERR_FAIL_INDEX_V(p_layer_id, physics.size(), Vector()); + ERR_FAIL_INDEX_V(p_polygon_index, physics[p_layer_id].polygons.size(), Vector()); + return physics[p_layer_id].polygons[p_polygon_index].polygon; +} + +void TileData::set_collision_polygon_one_way(int p_layer_id, int p_polygon_index, bool p_one_way) { + ERR_FAIL_INDEX(p_layer_id, physics.size()); + ERR_FAIL_INDEX(p_polygon_index, physics[p_layer_id].polygons.size()); + physics.write[p_layer_id].polygons.write[p_polygon_index].one_way = p_one_way; + emit_signal("changed"); +} + +bool TileData::is_collision_polygon_one_way(int p_layer_id, int p_polygon_index) const { + ERR_FAIL_INDEX_V(p_layer_id, physics.size(), false); + ERR_FAIL_INDEX_V(p_polygon_index, physics[p_layer_id].polygons.size(), false); + return physics[p_layer_id].polygons[p_polygon_index].one_way; +} + +void TileData::set_collision_polygon_one_way_margin(int p_layer_id, int p_polygon_index, float p_one_way_margin) { + ERR_FAIL_INDEX(p_layer_id, physics.size()); + ERR_FAIL_INDEX(p_polygon_index, physics[p_layer_id].polygons.size()); + physics.write[p_layer_id].polygons.write[p_polygon_index].one_way_margin = p_one_way_margin; + emit_signal("changed"); +} + +float TileData::get_collision_polygon_one_way_margin(int p_layer_id, int p_polygon_index) const { + ERR_FAIL_INDEX_V(p_layer_id, physics.size(), 0.0); + ERR_FAIL_INDEX_V(p_polygon_index, physics[p_layer_id].polygons.size(), 0.0); + return physics[p_layer_id].polygons[p_polygon_index].one_way_margin; +} + +int TileData::get_collision_polygon_shapes_count(int p_layer_id, int p_polygon_index) const { + ERR_FAIL_INDEX_V(p_layer_id, physics.size(), 0); + ERR_FAIL_INDEX_V(p_polygon_index, physics[p_layer_id].polygons.size(), 0); + return physics[p_layer_id].polygons[p_polygon_index].shapes.size(); +} + +Ref TileData::get_collision_polygon_shape(int p_layer_id, int p_polygon_index, int shape_index) const { + ERR_FAIL_INDEX_V(p_layer_id, physics.size(), 0); + ERR_FAIL_INDEX_V(p_polygon_index, physics[p_layer_id].polygons.size(), Ref()); + ERR_FAIL_INDEX_V(shape_index, (int)physics[p_layer_id].polygons[shape_index].shapes.size(), Ref()); + return physics[p_layer_id].polygons[shape_index].shapes[shape_index]; +} + +// Terrain +void TileData::set_terrain_set(int p_terrain_set) { + ERR_FAIL_COND(p_terrain_set < -1); + if (p_terrain_set == terrain_set) { + return; + } + if (tile_set) { + ERR_FAIL_COND(p_terrain_set >= tile_set->get_terrain_sets_count()); + for (int i = 0; i < 16; i++) { + terrain_peering_bits[i] = -1; } } + terrain_set = p_terrain_set; + notify_property_list_changed(); + emit_signal("changed"); +} - int half_polygon_size = polygon.size(); - for (int i = 0; i < half_polygon_size; i++) { - polygon.push_back(polygon[half_polygon_size - 1 - i] / 3.0); +int TileData::get_terrain_set() const { + return terrain_set; +} + +void TileData::set_peering_bit_terrain(TileSet::CellNeighbor p_peering_bit, int p_terrain_index) { + ERR_FAIL_COND(terrain_set < 0); + ERR_FAIL_COND(p_terrain_index < -1); + if (tile_set) { + ERR_FAIL_COND(p_terrain_index >= tile_set->get_terrains_count(terrain_set)); + ERR_FAIL_COND(!is_valid_peering_bit_terrain(p_peering_bit)); } + terrain_peering_bits[p_peering_bit] = p_terrain_index; + emit_signal("changed"); +} + +int TileData::get_peering_bit_terrain(TileSet::CellNeighbor p_peering_bit) const { + ERR_FAIL_COND_V(!is_valid_peering_bit_terrain(p_peering_bit), -1); + return terrain_peering_bits[p_peering_bit]; +} + +bool TileData::is_valid_peering_bit_terrain(TileSet::CellNeighbor p_peering_bit) const { + ERR_FAIL_COND_V(!tile_set, false); + + return tile_set->is_valid_peering_bit_terrain(terrain_set, p_peering_bit); +} - if (!polygon.is_empty()) { - p_canvas_item->draw_polygon(polygon, color_array); - } +// Navigation +void TileData::set_navigation_polygon(int p_layer_id, Ref p_navigation_polygon) { + ERR_FAIL_INDEX(p_layer_id, navigation.size()); + navigation.write[p_layer_id] = p_navigation_polygon; + emit_signal("changed"); } -void TileSetPluginAtlasTerrain::_draw_half_offset_corner_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis) { - PackedColorArray color_array; - color_array.push_back(p_color); +Ref TileData::get_navigation_polygon(int p_layer_id) const { + ERR_FAIL_INDEX_V(p_layer_id, navigation.size(), Ref()); + return navigation[p_layer_id]; +} - PackedVector2Array point_list; - point_list.push_back(Vector2(3, 0)); - point_list.push_back(Vector2(3, 3.0 * (1.0 - p_overlap * 2.0))); - point_list.push_back(Vector2(1.5, (3.0 * (1.0 - p_overlap * 2.0) + 3.0) / 2.0)); - point_list.push_back(Vector2(0, 3)); - point_list.push_back(Vector2(-1.5, (3.0 * (1.0 - p_overlap * 2.0) + 3.0) / 2.0)); - point_list.push_back(Vector2(-3, 3.0 * (1.0 - p_overlap * 2.0))); - point_list.push_back(Vector2(-3, 0)); - point_list.push_back(Vector2(-3, -3.0 * (1.0 - p_overlap * 2.0))); - point_list.push_back(Vector2(-1.5, -(3.0 * (1.0 - p_overlap * 2.0) + 3.0) / 2.0)); - point_list.push_back(Vector2(0, -3)); - point_list.push_back(Vector2(1.5, -(3.0 * (1.0 - p_overlap * 2.0) + 3.0) / 2.0)); - point_list.push_back(Vector2(3, -3.0 * (1.0 - p_overlap * 2.0))); +// Misc +void TileData::set_probability(float p_probability) { + ERR_FAIL_COND(p_probability < 0.0); + probability = p_probability; + emit_signal("changed"); +} +float TileData::get_probability() const { + return probability; +} - Vector2 unit = Vector2(p_size) / 6.0; - for (int i = 0; i < point_list.size(); i++) { - point_list.write[i] = point_list[i] * unit; - } +// Custom data +void TileData::set_custom_data(String p_layer_name, Variant p_value) { + ERR_FAIL_COND(!tile_set); + int p_layer_id = tile_set->get_custom_data_layer_by_name(p_layer_name); + ERR_FAIL_COND_MSG(p_layer_id < 0, vformat("TileSet has no layer with name: %s", p_layer_name)); + set_custom_data_by_layer_id(p_layer_id, p_value); +} - PackedVector2Array polygon; - if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { - switch (p_bit) { - case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: - polygon.push_back(point_list[0]); - polygon.push_back(point_list[1]); - polygon.push_back(point_list[2]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER: - polygon.push_back(point_list[2]); - polygon.push_back(point_list[3]); - polygon.push_back(point_list[4]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER: - polygon.push_back(point_list[4]); - polygon.push_back(point_list[5]); - polygon.push_back(point_list[6]); - break; - case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER: - polygon.push_back(point_list[6]); - polygon.push_back(point_list[7]); - polygon.push_back(point_list[8]); - break; - case TileSet::CELL_NEIGHBOR_TOP_CORNER: - polygon.push_back(point_list[8]); - polygon.push_back(point_list[9]); - polygon.push_back(point_list[10]); - break; - case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER: - polygon.push_back(point_list[10]); - polygon.push_back(point_list[11]); - polygon.push_back(point_list[0]); - break; - default: - break; - } - } else { - if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL) { - for (int i = 0; i < point_list.size(); i++) { - point_list.write[i] = Vector2(point_list[i].y, point_list[i].x); - } - } - switch (p_bit) { - case TileSet::CELL_NEIGHBOR_RIGHT_CORNER: - polygon.push_back(point_list[2]); - polygon.push_back(point_list[3]); - polygon.push_back(point_list[4]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: - polygon.push_back(point_list[0]); - polygon.push_back(point_list[1]); - polygon.push_back(point_list[2]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER: - polygon.push_back(point_list[10]); - polygon.push_back(point_list[11]); - polygon.push_back(point_list[0]); - break; - case TileSet::CELL_NEIGHBOR_LEFT_CORNER: - polygon.push_back(point_list[8]); - polygon.push_back(point_list[9]); - polygon.push_back(point_list[10]); - break; - case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER: - polygon.push_back(point_list[6]); - polygon.push_back(point_list[7]); - polygon.push_back(point_list[8]); - break; - case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER: - polygon.push_back(point_list[4]); - polygon.push_back(point_list[5]); - polygon.push_back(point_list[6]); - break; - default: - break; - } - } +Variant TileData::get_custom_data(String p_layer_name) const { + ERR_FAIL_COND_V(!tile_set, Variant()); + int p_layer_id = tile_set->get_custom_data_layer_by_name(p_layer_name); + ERR_FAIL_COND_V_MSG(p_layer_id < 0, Variant(), vformat("TileSet has no layer with name: %s", p_layer_name)); + return get_custom_data_by_layer_id(p_layer_id); +} - int half_polygon_size = polygon.size(); - for (int i = 0; i < half_polygon_size; i++) { - polygon.push_back(polygon[half_polygon_size - 1 - i] / 3.0); - } +void TileData::set_custom_data_by_layer_id(int p_layer_id, Variant p_value) { + ERR_FAIL_INDEX(p_layer_id, custom_data.size()); + custom_data.write[p_layer_id] = p_value; + emit_signal("changed"); +} - if (!polygon.is_empty()) { - p_canvas_item->draw_polygon(polygon, color_array); - } +Variant TileData::get_custom_data_by_layer_id(int p_layer_id) const { + ERR_FAIL_INDEX_V(p_layer_id, custom_data.size(), Variant()); + return custom_data[p_layer_id]; } -void TileSetPluginAtlasTerrain::_draw_half_offset_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis) { - PackedColorArray color_array; - color_array.push_back(p_color); +bool TileData::_set(const StringName &p_name, const Variant &p_value) { + Vector components = String(p_name).split("/", true, 2); - PackedVector2Array point_list; - point_list.push_back(Vector2(3, 3.0 * (1.0 - p_overlap * 2.0))); - point_list.push_back(Vector2(0, 3)); - point_list.push_back(Vector2(-3, 3.0 * (1.0 - p_overlap * 2.0))); - point_list.push_back(Vector2(-3, -3.0 * (1.0 - p_overlap * 2.0))); - point_list.push_back(Vector2(0, -3)); - point_list.push_back(Vector2(3, -3.0 * (1.0 - p_overlap * 2.0))); + if (components.size() == 2 && components[0].begins_with("occlusion_layer_") && components[0].trim_prefix("occlusion_layer_").is_valid_int()) { + // Occlusion layers. + int layer_index = components[0].trim_prefix("occlusion_layer_").to_int(); + ERR_FAIL_COND_V(layer_index < 0, false); + if (components[1] == "polygon") { + Ref polygon = p_value; + if (!polygon.is_valid()) { + return false; + } - Vector2 unit = Vector2(p_size) / 6.0; - for (int i = 0; i < point_list.size(); i++) { - point_list.write[i] = point_list[i] * unit; - } + if (layer_index >= occluders.size()) { + if (tile_set) { + return false; + } else { + occluders.resize(layer_index + 1); + } + } + set_occluder(layer_index, polygon); + return true; + } + } else if (components.size() >= 2 && components[0].begins_with("physics_layer_") && components[0].trim_prefix("physics_layer_").is_valid_int()) { + // Physics layers. + int layer_index = components[0].trim_prefix("physics_layer_").to_int(); + ERR_FAIL_COND_V(layer_index < 0, false); + if (components.size() == 2 && components[1] == "polygons_count") { + if (p_value.get_type() != Variant::INT) { + return false; + } - PackedVector2Array polygon; - if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { - switch (p_bit) { - case TileSet::CELL_NEIGHBOR_RIGHT_SIDE: - polygon.push_back(point_list[5]); - polygon.push_back(point_list[0]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE: - polygon.push_back(point_list[0]); - polygon.push_back(point_list[1]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE: - polygon.push_back(point_list[1]); - polygon.push_back(point_list[2]); - break; - case TileSet::CELL_NEIGHBOR_LEFT_SIDE: - polygon.push_back(point_list[2]); - polygon.push_back(point_list[3]); - break; - case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE: - polygon.push_back(point_list[3]); - polygon.push_back(point_list[4]); - break; - case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE: - polygon.push_back(point_list[4]); - polygon.push_back(point_list[5]); - break; - default: - break; + if (layer_index >= physics.size()) { + if (tile_set) { + return false; + } else { + physics.resize(layer_index + 1); + } + } + set_collision_polygons_count(layer_index, p_value); + return true; + } else if (components.size() == 3 && components[1].begins_with("polygon_") && components[1].trim_prefix("polygon_").is_valid_int()) { + int polygon_index = components[1].trim_prefix("polygon_").to_int(); + ERR_FAIL_COND_V(polygon_index < 0, false); + + if (components[2] == "points" || components[2] == "one_way" || components[2] == "one_way_margin") { + if (layer_index >= physics.size()) { + if (tile_set) { + return false; + } else { + physics.resize(layer_index + 1); + } + } + + if (polygon_index >= physics[layer_index].polygons.size()) { + physics.write[layer_index].polygons.resize(polygon_index + 1); + } + } + if (components[2] == "points") { + Vector polygon = p_value; + set_collision_polygon_points(layer_index, polygon_index, polygon); + return true; + } else if (components[2] == "one_way") { + set_collision_polygon_one_way(layer_index, polygon_index, p_value); + return true; + } else if (components[2] == "one_way_margin") { + set_collision_polygon_one_way_margin(layer_index, polygon_index, p_value); + return true; + } } - } else { - if (p_offset_axis == TileSet::TILE_OFFSET_AXIS_VERTICAL) { - for (int i = 0; i < point_list.size(); i++) { - point_list.write[i] = Vector2(point_list[i].y, point_list[i].x); + } else if (components.size() == 2 && components[0].begins_with("navigation_layer_") && components[0].trim_prefix("navigation_layer_").is_valid_int()) { + // Navigation layers. + int layer_index = components[0].trim_prefix("navigation_layer_").to_int(); + ERR_FAIL_COND_V(layer_index < 0, false); + if (components[1] == "polygon") { + Ref polygon = p_value; + if (!polygon.is_valid()) { + return false; + } + + if (layer_index >= navigation.size()) { + if (tile_set) { + return false; + } else { + navigation.resize(layer_index + 1); + } + } + set_navigation_polygon(layer_index, polygon); + return true; + } + } else if (components.size() == 2 && components[0] == "terrains_peering_bit") { + // Terrains. + for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { + TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); + if (components[1] == TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]) { + set_peering_bit_terrain(bit, p_value); + return true; } } - switch (p_bit) { - case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE: - polygon.push_back(point_list[0]); - polygon.push_back(point_list[1]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE: - polygon.push_back(point_list[5]); - polygon.push_back(point_list[0]); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE: - polygon.push_back(point_list[4]); - polygon.push_back(point_list[5]); - break; - case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE: - polygon.push_back(point_list[3]); - polygon.push_back(point_list[4]); - break; - case TileSet::CELL_NEIGHBOR_TOP_SIDE: - polygon.push_back(point_list[2]); - polygon.push_back(point_list[3]); - break; - case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE: - polygon.push_back(point_list[1]); - polygon.push_back(point_list[2]); - break; - default: - break; + return false; + } else if (components.size() == 1 && components[0].begins_with("custom_data_") && components[0].trim_prefix("custom_data_").is_valid_int()) { + // Custom data layers. + int layer_index = components[0].trim_prefix("custom_data_").to_int(); + ERR_FAIL_COND_V(layer_index < 0, false); + + if (layer_index >= custom_data.size()) { + if (tile_set) { + return false; + } else { + custom_data.resize(layer_index + 1); + } } - } + set_custom_data_by_layer_id(layer_index, p_value); - int half_polygon_size = polygon.size(); - for (int i = 0; i < half_polygon_size; i++) { - polygon.push_back(polygon[half_polygon_size - 1 - i] / 3.0); + return true; } - if (!polygon.is_empty()) { - p_canvas_item->draw_polygon(polygon, color_array); - } + return false; } -#define TERRAIN_ALPHA 0.8 +bool TileData::_get(const StringName &p_name, Variant &r_ret) const { + Vector components = String(p_name).split("/", true, 2); -#define DRAW_TERRAIN_BIT(f, bit) \ - { \ - int terrain_id = p_tile_data->get_peering_bit_terrain((bit)); \ - if (terrain_id >= 0) { \ - Color color = p_tile_set->get_terrain_color(terrain_set, terrain_id); \ - color.a = TERRAIN_ALPHA; \ - f(p_canvas_item, color, size, (bit)); \ - } \ + if (tile_set) { + if (components.size() == 2 && components[0].begins_with("occlusion_layer") && components[0].trim_prefix("occlusion_layer_").is_valid_int()) { + // Occlusion layers. + int layer_index = components[0].trim_prefix("occlusion_layer_").to_int(); + ERR_FAIL_COND_V(layer_index < 0, false); + if (layer_index >= occluders.size()) { + return false; + } + if (components[1] == "polygon") { + r_ret = get_occluder(layer_index); + return true; + } + } else if (components.size() >= 2 && components[0].begins_with("physics_layer_") && components[0].trim_prefix("physics_layer_").is_valid_int()) { + // Physics layers. + int layer_index = components[0].trim_prefix("physics_layer_").to_int(); + ERR_FAIL_COND_V(layer_index < 0, false); + if (layer_index >= physics.size()) { + return false; + } + if (components.size() == 2 && components[1] == "polygons_count") { + r_ret = get_collision_polygons_count(layer_index); + return true; + } else if (components.size() == 3 && components[1].begins_with("polygon_") && components[1].trim_prefix("polygon_").is_valid_int()) { + int polygon_index = components[1].trim_prefix("polygon_").to_int(); + ERR_FAIL_COND_V(polygon_index < 0, false); + if (polygon_index >= physics[layer_index].polygons.size()) { + return false; + } + if (components[2] == "points") { + r_ret = get_collision_polygon_points(layer_index, polygon_index); + return true; + } else if (components[2] == "one_way") { + r_ret = is_collision_polygon_one_way(layer_index, polygon_index); + return true; + } else if (components[2] == "one_way_margin") { + r_ret = get_collision_polygon_one_way_margin(layer_index, polygon_index); + return true; + } + } + } else if (components.size() == 2 && components[0] == "terrains_peering_bit") { + // Terrains. + for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { + if (components[1] == TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]) { + r_ret = terrain_peering_bits[i]; + return true; + } + } + return false; + } else if (components.size() == 2 && components[0].begins_with("navigation_layer_") && components[0].trim_prefix("navigation_layer_").is_valid_int()) { + // Occlusion layers. + int layer_index = components[0].trim_prefix("navigation_layer_").to_int(); + ERR_FAIL_COND_V(layer_index < 0, false); + if (layer_index >= navigation.size()) { + return false; + } + if (components[1] == "polygon") { + r_ret = get_navigation_polygon(layer_index); + return true; + } + } else if (components.size() == 1 && components[0].begins_with("custom_data_") && components[0].trim_prefix("custom_data_").is_valid_int()) { + // Custom data layers. + int layer_index = components[0].trim_prefix("custom_data_").to_int(); + ERR_FAIL_COND_V(layer_index < 0, false); + if (layer_index >= custom_data.size()) { + return false; + } + r_ret = get_custom_data_by_layer_id(layer_index); + return true; + } } -#define DRAW_HALF_OFFSET_TERRAIN_BIT(f, bit, overlap, half_offset_axis) \ - { \ - int terrain_id = p_tile_data->get_peering_bit_terrain((bit)); \ - if (terrain_id >= 0) { \ - Color color = p_tile_set->get_terrain_color(terrain_set, terrain_id); \ - color.a = TERRAIN_ALPHA; \ - f(p_canvas_item, color, size, (bit), overlap, half_offset_axis); \ - } \ - } + return false; +} -void TileSetPluginAtlasTerrain::draw_terrains(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, const TileData *p_tile_data) { - ERR_FAIL_COND(!p_tile_set); - ERR_FAIL_COND(!p_tile_data); +void TileData::_get_property_list(List *p_list) const { + PropertyInfo property_info; + // Add the groups manually. + if (tile_set) { + // Occlusion layers. + p_list->push_back(PropertyInfo(Variant::NIL, "Rendering", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP)); + for (int i = 0; i < occluders.size(); i++) { + // occlusion_layer_%d/polygon + property_info = PropertyInfo(Variant::OBJECT, vformat("occlusion_layer_%d/polygon", i), PROPERTY_HINT_RESOURCE_TYPE, "OccluderPolygon2D", PROPERTY_USAGE_DEFAULT); + if (!occluders[i].is_valid()) { + property_info.usage ^= PROPERTY_USAGE_STORAGE; + } + p_list->push_back(property_info); + } - int terrain_set = p_tile_data->get_terrain_set(); - if (terrain_set < 0) { - return; - } - TileSet::TerrainMode terrain_mode = p_tile_set->get_terrain_set_mode(terrain_set); + // Physics layers. + p_list->push_back(PropertyInfo(Variant::NIL, "Physics", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP)); + for (int i = 0; i < physics.size(); i++) { + p_list->push_back(PropertyInfo(Variant::INT, vformat("physics_layer_%d/polygons_count", i), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR)); - TileSet::TileShape shape = p_tile_set->get_tile_shape(); - Vector2i size = p_tile_set->get_tile_size(); + for (int j = 0; j < physics[i].polygons.size(); j++) { + // physics_layer_%d/points + property_info = PropertyInfo(Variant::ARRAY, vformat("physics_layer_%d/polygon_%d/points", i, j), PROPERTY_HINT_ARRAY_TYPE, "Vector2", PROPERTY_USAGE_DEFAULT); + if (physics[i].polygons[j].polygon.is_empty()) { + property_info.usage ^= PROPERTY_USAGE_STORAGE; + } + p_list->push_back(property_info); - RenderingServer::get_singleton()->canvas_item_add_set_transform(p_canvas_item->get_canvas_item(), p_transform); - if (shape == TileSet::TILE_SHAPE_SQUARE) { - if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES) { - DRAW_TERRAIN_BIT(_draw_square_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_RIGHT_SIDE); - DRAW_TERRAIN_BIT(_draw_square_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER); - DRAW_TERRAIN_BIT(_draw_square_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE); - DRAW_TERRAIN_BIT(_draw_square_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER); - DRAW_TERRAIN_BIT(_draw_square_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_LEFT_SIDE); - DRAW_TERRAIN_BIT(_draw_square_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER); - DRAW_TERRAIN_BIT(_draw_square_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_SIDE); - DRAW_TERRAIN_BIT(_draw_square_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER); - } else if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { - DRAW_TERRAIN_BIT(_draw_square_corner_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER); - DRAW_TERRAIN_BIT(_draw_square_corner_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER); - DRAW_TERRAIN_BIT(_draw_square_corner_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER); - DRAW_TERRAIN_BIT(_draw_square_corner_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER); - } else { // TileData::TERRAIN_MODE_MATCH_SIDES - DRAW_TERRAIN_BIT(_draw_square_side_terrain_bit, TileSet::CELL_NEIGHBOR_RIGHT_SIDE); - DRAW_TERRAIN_BIT(_draw_square_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE); - DRAW_TERRAIN_BIT(_draw_square_side_terrain_bit, TileSet::CELL_NEIGHBOR_LEFT_SIDE); - DRAW_TERRAIN_BIT(_draw_square_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_SIDE); - } - } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC) { - if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES) { - DRAW_TERRAIN_BIT(_draw_isometric_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_RIGHT_CORNER); - DRAW_TERRAIN_BIT(_draw_isometric_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE); - DRAW_TERRAIN_BIT(_draw_isometric_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_CORNER); - DRAW_TERRAIN_BIT(_draw_isometric_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE); - DRAW_TERRAIN_BIT(_draw_isometric_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_LEFT_CORNER); - DRAW_TERRAIN_BIT(_draw_isometric_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE); - DRAW_TERRAIN_BIT(_draw_isometric_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_CORNER); - DRAW_TERRAIN_BIT(_draw_isometric_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE); - } else if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { - DRAW_TERRAIN_BIT(_draw_isometric_corner_terrain_bit, TileSet::CELL_NEIGHBOR_RIGHT_CORNER); - DRAW_TERRAIN_BIT(_draw_isometric_corner_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_CORNER); - DRAW_TERRAIN_BIT(_draw_isometric_corner_terrain_bit, TileSet::CELL_NEIGHBOR_LEFT_CORNER); - DRAW_TERRAIN_BIT(_draw_isometric_corner_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_CORNER); - } else { // TileData::TERRAIN_MODE_MATCH_SIDES - DRAW_TERRAIN_BIT(_draw_isometric_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE); - DRAW_TERRAIN_BIT(_draw_isometric_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE); - DRAW_TERRAIN_BIT(_draw_isometric_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE); - DRAW_TERRAIN_BIT(_draw_isometric_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE); - } - } else { - TileSet::TileOffsetAxis offset_axis = p_tile_set->get_tile_offset_axis(); - float overlap = 0.0; - switch (p_tile_set->get_tile_shape()) { - case TileSet::TILE_SHAPE_HEXAGON: - overlap = 0.25; - break; - case TileSet::TILE_SHAPE_HALF_OFFSET_SQUARE: - overlap = 0.0; - break; - default: - break; + // physics_layer_%d/polygon_%d/one_way + property_info = PropertyInfo(Variant::BOOL, vformat("physics_layer_%d/polygon_%d/one_way", i, j)); + if (physics[i].polygons[j].one_way == false) { + property_info.usage ^= PROPERTY_USAGE_STORAGE; + } + p_list->push_back(property_info); + + // physics_layer_%d/polygon_%d/one_way_margin + property_info = PropertyInfo(Variant::FLOAT, vformat("physics_layer_%d/polygon_%d/one_way_margin", i, j)); + if (physics[i].polygons[j].one_way_margin == 1.0) { + property_info.usage ^= PROPERTY_USAGE_STORAGE; + } + p_list->push_back(property_info); + } } - if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES) { - if (offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_RIGHT_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_LEFT_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER, overlap, offset_axis); - } else { - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_RIGHT_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_LEFT_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_or_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE, overlap, offset_axis); + + // Terrain data + if (terrain_set >= 0) { + p_list->push_back(PropertyInfo(Variant::NIL, "Terrains", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP)); + for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { + TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); + if (is_valid_peering_bit_terrain(bit)) { + property_info = PropertyInfo(Variant::INT, "terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i])); + if (get_peering_bit_terrain(bit) == -1) { + property_info.usage ^= PROPERTY_USAGE_STORAGE; + } + p_list->push_back(property_info); + } } - } else if (terrain_mode == TileSet::TERRAIN_MODE_MATCH_CORNERS) { - if (offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER, overlap, offset_axis); - } else { - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_RIGHT_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_LEFT_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_corner_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER, overlap, offset_axis); + } + + // Navigation layers. + p_list->push_back(PropertyInfo(Variant::NIL, "Navigation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP)); + for (int i = 0; i < navigation.size(); i++) { + property_info = PropertyInfo(Variant::OBJECT, vformat("navigation_layer_%d/polygon", i), PROPERTY_HINT_RESOURCE_TYPE, "NavigationPolygon", PROPERTY_USAGE_DEFAULT); + if (!navigation[i].is_valid()) { + property_info.usage ^= PROPERTY_USAGE_STORAGE; } - } else { // TileData::TERRAIN_MODE_MATCH_SIDES - if (offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_RIGHT_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_LEFT_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE, overlap, offset_axis); - } else { - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_SIDE, overlap, offset_axis); - DRAW_HALF_OFFSET_TERRAIN_BIT(_draw_half_offset_side_terrain_bit, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE, overlap, offset_axis); + p_list->push_back(property_info); + } + + // Custom data layers. + p_list->push_back(PropertyInfo(Variant::NIL, "Custom data", PROPERTY_HINT_NONE, "custom_data_", PROPERTY_USAGE_GROUP)); + for (int i = 0; i < custom_data.size(); i++) { + Variant default_val; + Callable::CallError error; + Variant::construct(custom_data[i].get_type(), default_val, nullptr, 0, error); + property_info = PropertyInfo(tile_set->get_custom_data_type(i), vformat("custom_data_%d", i), PROPERTY_HINT_NONE, String(), PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT); + if (custom_data[i] == default_val) { + property_info.usage ^= PROPERTY_USAGE_STORAGE; } + p_list->push_back(property_info); } } - RenderingServer::get_singleton()->canvas_item_add_set_transform(p_canvas_item->get_canvas_item(), Transform2D()); } +void TileData::_bind_methods() { + // Rendering. + ClassDB::bind_method(D_METHOD("set_flip_h", "flip_h"), &TileData::set_flip_h); + ClassDB::bind_method(D_METHOD("get_flip_h"), &TileData::get_flip_h); + ClassDB::bind_method(D_METHOD("set_flip_v", "flip_v"), &TileData::set_flip_v); + ClassDB::bind_method(D_METHOD("get_flip_v"), &TileData::get_flip_v); + ClassDB::bind_method(D_METHOD("set_transpose", "transpose"), &TileData::set_transpose); + ClassDB::bind_method(D_METHOD("get_transpose"), &TileData::get_transpose); + ClassDB::bind_method(D_METHOD("tile_set_material", "material"), &TileData::tile_set_material); + ClassDB::bind_method(D_METHOD("tile_get_material"), &TileData::tile_get_material); + ClassDB::bind_method(D_METHOD("set_texture_offset", "texture_offset"), &TileData::set_texture_offset); + ClassDB::bind_method(D_METHOD("get_texture_offset"), &TileData::get_texture_offset); + ClassDB::bind_method(D_METHOD("set_modulate", "modulate"), &TileData::set_modulate); + ClassDB::bind_method(D_METHOD("get_modulate"), &TileData::get_modulate); + ClassDB::bind_method(D_METHOD("set_z_index", "z_index"), &TileData::set_z_index); + ClassDB::bind_method(D_METHOD("get_z_index"), &TileData::get_z_index); + ClassDB::bind_method(D_METHOD("set_y_sort_origin", "y_sort_origin"), &TileData::set_y_sort_origin); + ClassDB::bind_method(D_METHOD("get_y_sort_origin"), &TileData::get_y_sort_origin); + + ClassDB::bind_method(D_METHOD("set_occluder", "layer_id", "occluder_polygon"), &TileData::set_occluder); + ClassDB::bind_method(D_METHOD("get_occluder", "layer_id"), &TileData::get_occluder); + + // Physics. + ClassDB::bind_method(D_METHOD("get_collision_polygons_count", "layer_id"), &TileData::get_collision_polygons_count); + ClassDB::bind_method(D_METHOD("set_collision_polygons_count", "layer_id", "polygons_count"), &TileData::set_collision_polygons_count); + ClassDB::bind_method(D_METHOD("add_collision_polygon", "layer_id"), &TileData::add_collision_polygon); + ClassDB::bind_method(D_METHOD("remove_collision_polygon", "layer_id", "polygon_index"), &TileData::remove_collision_polygon); + ClassDB::bind_method(D_METHOD("set_collision_polygon_points", "layer_id", "polygon_index", "polygon"), &TileData::set_collision_polygon_points); + ClassDB::bind_method(D_METHOD("get_collision_polygon_points", "layer_id", "polygon_index"), &TileData::get_collision_polygon_points); + ClassDB::bind_method(D_METHOD("set_collision_polygon_one_way", "layer_id", "polygon_index", "one_way"), &TileData::set_collision_polygon_one_way); + ClassDB::bind_method(D_METHOD("is_collision_polygon_one_way", "layer_id", "polygon_index"), &TileData::is_collision_polygon_one_way); + ClassDB::bind_method(D_METHOD("set_collision_polygon_one_way_margin", "layer_id", "polygon_index", "one_way_margin"), &TileData::set_collision_polygon_one_way_margin); + ClassDB::bind_method(D_METHOD("get_collision_polygon_one_way_margin", "layer_id", "polygon_index"), &TileData::get_collision_polygon_one_way_margin); + + // Terrain + ClassDB::bind_method(D_METHOD("set_terrain_set", "terrain_set"), &TileData::set_terrain_set); + ClassDB::bind_method(D_METHOD("get_terrain_set"), &TileData::get_terrain_set); + ClassDB::bind_method(D_METHOD("set_peering_bit_terrain", "peering_bit", "terrain"), &TileData::set_peering_bit_terrain); + ClassDB::bind_method(D_METHOD("get_peering_bit_terrain", "peering_bit"), &TileData::get_peering_bit_terrain); + + // Navigation + ClassDB::bind_method(D_METHOD("set_navigation_polygon", "layer_id", "navigation_polygon"), &TileData::set_navigation_polygon); + ClassDB::bind_method(D_METHOD("get_navigation_polygon", "layer_id"), &TileData::get_navigation_polygon); + + // Misc. + ClassDB::bind_method(D_METHOD("set_probability", "probability"), &TileData::set_probability); + ClassDB::bind_method(D_METHOD("get_probability"), &TileData::get_probability); + + // Custom data. + ClassDB::bind_method(D_METHOD("set_custom_data", "layer_name", "value"), &TileData::set_custom_data); + ClassDB::bind_method(D_METHOD("get_custom_data", "layer_name"), &TileData::get_custom_data); + ClassDB::bind_method(D_METHOD("set_custom_data_by_layer_id", "layer_id", "value"), &TileData::set_custom_data_by_layer_id); + ClassDB::bind_method(D_METHOD("get_custom_data_by_layer_id", "layer_id"), &TileData::get_custom_data_by_layer_id); + + ADD_GROUP("Rendering", ""); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_h"), "set_flip_h", "get_flip_h"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_v"), "set_flip_v", "get_flip_v"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "transpose"), "set_transpose", "get_transpose"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "texture_offset"), "set_texture_offset", "get_texture_offset"); + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "modulate"), "set_modulate", "get_modulate"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "z_index"), "set_z_index", "get_z_index"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "y_sort_origin"), "set_y_sort_origin", "get_y_sort_origin"); + + ADD_GROUP("Terrains", ""); + ADD_PROPERTY(PropertyInfo(Variant::INT, "terrain_set"), "set_terrain_set", "get_terrain_set"); + + ADD_GROUP("Miscellaneous", ""); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "probability"), "set_probability", "get_probability"); + + ADD_SIGNAL(MethodInfo("changed")); +} /////////////////////////////// TileSetPluginAtlasRendering ////////////////////////////////////// void TileSetPluginAtlasRendering::tilemap_notification(TileMap *p_tile_map, int p_what) { @@ -4218,15 +4167,17 @@ void TileSetPluginAtlasPhysics::update_dirty_quadrants(TileMap *p_tile_map, Self for (int body_index = 0; body_index < q.bodies.size(); body_index++) { // Add the shapes again. - for (int shape_index = 0; shape_index < tile_data->get_collision_shapes_count(body_index); shape_index++) { - bool one_way_collision = tile_data->is_collision_shape_one_way(body_index, shape_index); - float one_way_collision_margin = tile_data->get_collision_shape_one_way_margin(body_index, shape_index); - Ref shape = tile_data->get_collision_shape_shape(body_index, shape_index); - if (shape.is_valid()) { + for (int polygon_index = 0; polygon_index < tile_data->get_collision_polygons_count(body_index); polygon_index++) { + bool one_way_collision = tile_data->is_collision_polygon_one_way(body_index, polygon_index); + float one_way_collision_margin = tile_data->get_collision_polygon_one_way_margin(body_index, polygon_index); + + int shapes_count = tile_data->get_collision_polygon_shapes_count(body_index, polygon_index); + for (int shape_index = 0; shape_index < shapes_count; shape_index++) { Transform2D xform = Transform2D(); xform.set_origin(p_tile_map->map_to_world(E_cell->get()) - quadrant_pos); // Add decomposed convex shapes. + Ref shape = tile_data->get_collision_polygon_shape(body_index, polygon_index, shape_index); ps->body_add_shape(q.bodies[body_index], shape->get_rid(), xform); ps->body_set_shape_metadata(q.bodies[body_index], shape_index, E_cell->get()); ps->body_set_shape_as_one_way_collision(q.bodies[body_index], shape_index, one_way_collision, one_way_collision_margin); @@ -4343,11 +4294,13 @@ void TileSetPluginAtlasPhysics::draw_quadrant_debug(TileMap *p_tile_map, TileMap TileData *tile_data = Object::cast_to(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile)); for (int body_index = 0; body_index < p_quadrant->bodies.size(); body_index++) { - for (int shape_index = 0; shape_index < tile_data->get_collision_shapes_count(body_index); shape_index++) { - // Draw the debug shape. - Ref shape = tile_data->get_collision_shape_shape(body_index, shape_index); - if (shape.is_valid()) { - shape->draw(p_quadrant->debug_canvas_item, debug_collision_color); + for (int polygon_index = 0; polygon_index < tile_data->get_collision_polygons_count(body_index); polygon_index++) { + // Draw the debug polygon. + Vector polygon = tile_data->get_collision_polygon_points(body_index, polygon_index); + if (polygon.size() >= 3) { + Vector color; + color.push_back(debug_collision_color); + rs->canvas_item_add_polygon(p_quadrant->debug_canvas_item, polygon, color); } } } diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h index 6cf4198f30..dbf6dbabe6 100644 --- a/scene/resources/tile_set.h +++ b/scene/resources/tile_set.h @@ -33,19 +33,18 @@ #include "core/io/resource.h" #include "core/object/object.h" +#include "core/templates/local_vector.h" #include "scene/2d/light_occluder_2d.h" #include "scene/2d/navigation_region_2d.h" #include "scene/main/canvas_item.h" +#include "scene/resources/concave_polygon_shape_2d.h" #include "scene/resources/convex_polygon_shape_2d.h" #include "scene/resources/packed_scene.h" #include "scene/resources/physics_material.h" #include "scene/resources/shape_2d.h" #ifndef DISABLE_DEPRECATED -#include "scene/2d/light_occluder_2d.h" -#include "scene/2d/navigation_region_2d.h" #include "scene/resources/shader.h" -#include "scene/resources/shape_2d.h" #include "scene/resources/texture.h" #endif @@ -60,7 +59,6 @@ class TileSetPlugin; class TileSetPluginAtlasRendering; class TileSetPluginAtlasPhysics; class TileSetPluginAtlasNavigation; -class TileSetPluginAtlasTerrain; class TileSet : public Resource { GDCLASS(TileSet, Resource); @@ -138,6 +136,8 @@ public: CELL_NEIGHBOR_MAX, }; + static const char *CELL_NEIGHBOR_ENUM_TO_TEXT[]; + enum TerrainMode { TERRAIN_MODE_MATCH_CORNERS_AND_SIDES = 0, TERRAIN_MODE_MATCH_CORNERS, @@ -194,6 +194,10 @@ private: }; Vector occlusion_layers; + Ref tile_lines_mesh; + Ref tile_filled_mesh; + bool tile_meshes_dirty = true; + // Physics struct PhysicsLayer { uint32_t collision_layer = 1; @@ -213,6 +217,9 @@ private: }; Vector terrain_sets; + Map>> terrain_bits_meshes; + bool terrain_bits_meshes_dirty = true; + // Navigation struct Navigationlayer { uint32_t layers = 1; @@ -239,6 +246,19 @@ private: void _compute_next_source_id(); void _source_changed(); + // Helpers + Vector _get_square_corner_or_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit); + Vector _get_square_corner_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit); + Vector _get_square_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit); + + Vector _get_isometric_corner_or_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit); + Vector _get_isometric_corner_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit); + Vector _get_isometric_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit); + + Vector _get_half_offset_corner_or_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis); + Vector _get_half_offset_corner_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis); + Vector _get_half_offset_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis); + protected: static void _bind_methods(); @@ -257,8 +277,6 @@ public: TileOffsetAxis get_tile_offset_axis() const; void set_tile_size(Size2i p_size); Size2i get_tile_size() const; - void set_tile_skew(Vector2 p_skew); - Vector2 get_tile_skew() const; // -- Sources management -- int get_next_source_id() const; @@ -305,6 +323,7 @@ public: String get_terrain_name(int p_terrain_set, int p_terrain_index) const; void set_terrain_color(int p_terrain_set, int p_terrain_index, Color p_color); Color get_terrain_color(int p_terrain_set, int p_terrain_index) const; + bool is_valid_peering_bit_for_mode(TileSet::TerrainMode p_terrain_mode, TileSet::CellNeighbor p_peering_bit) const; bool is_valid_peering_bit_terrain(int p_terrain_set, TileSet::CellNeighbor p_peering_bit) const; // Navigation @@ -323,8 +342,14 @@ public: Variant::Type get_custom_data_type(int p_layer_id) const; // Helpers + Vector get_tile_shape_polygon(); void draw_tile_shape(CanvasItem *p_canvas_item, Rect2 p_region, Color p_color, bool p_filled = false, Ref p_texture = Ref()); + Vector get_terrain_bit_polygon(int p_terrain_set, TileSet::CellNeighbor p_bit); + void draw_terrains(CanvasItem *p_canvas_item, Transform2D p_transform, const TileData *p_tile_data); + Vector>> generate_terrains_icons(Size2i p_size); + + // Resource management virtual void reset_state() override; TileSet(); @@ -509,13 +534,14 @@ private: // Physics struct PhysicsLayerTileData { - struct ShapeTileData { - Ref shape = Ref(); + struct PolygonShapeTileData { + LocalVector polygon; + LocalVector> shapes; bool one_way = false; float one_way_margin = 1.0; }; - Vector shapes; + Vector polygons; }; Vector physics; // TODO add support for areas. @@ -570,16 +596,18 @@ public: Ref get_occluder(int p_layer_id) const; // Physics - int get_collision_shapes_count(int p_layer_id) const; - void set_collision_shapes_count(int p_layer_id, int p_shapes_count); - void add_collision_shape(int p_layer_id); - void remove_collision_shape(int p_layer_id, int p_shape_index); - void set_collision_shape_shape(int p_layer_id, int p_shape_index, Ref p_shape); - Ref get_collision_shape_shape(int p_layer_id, int p_shape_index) const; - void set_collision_shape_one_way(int p_layer_id, int p_shape_index, bool p_one_way); - bool is_collision_shape_one_way(int p_layer_id, int p_shape_index) const; - void set_collision_shape_one_way_margin(int p_layer_id, int p_shape_index, float p_one_way_margin); - float get_collision_shape_one_way_margin(int p_layer_id, int p_shape_index) const; + int get_collision_polygons_count(int p_layer_id) const; + void set_collision_polygons_count(int p_layer_id, int p_shapes_count); + void add_collision_polygon(int p_layer_id); + void remove_collision_polygon(int p_layer_id, int p_polygon_index); + void set_collision_polygon_points(int p_layer_id, int p_polygon_index, Vector p_polygon); + Vector get_collision_polygon_points(int p_layer_id, int p_polygon_index) const; + void set_collision_polygon_one_way(int p_layer_id, int p_polygon_index, bool p_one_way); + bool is_collision_polygon_one_way(int p_layer_id, int p_polygon_index) const; + void set_collision_polygon_one_way_margin(int p_layer_id, int p_polygon_index, float p_one_way_margin); + float get_collision_polygon_one_way_margin(int p_layer_id, int p_polygon_index) const; + int get_collision_polygon_shapes_count(int p_layer_id, int p_polygon_index) const; + Ref get_collision_polygon_shape(int p_layer_id, int p_polygon_index, int shape_index) const; // Terrain void set_terrain_set(int p_terrain_id); @@ -637,26 +665,6 @@ public: static void draw_tile(RID p_canvas_item, Vector2i p_position, const Ref p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, Color p_modulation = Color(1.0, 1.0, 1.0, 1.0)); }; -class TileSetPluginAtlasTerrain : public TileSetPlugin { - GDCLASS(TileSetPluginAtlasTerrain, TileSetPlugin); - -private: - static void _draw_square_corner_or_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit); - static void _draw_square_corner_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit); - static void _draw_square_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit); - - static void _draw_isometric_corner_or_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit); - static void _draw_isometric_corner_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit); - static void _draw_isometric_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit); - - static void _draw_half_offset_corner_or_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis); - static void _draw_half_offset_corner_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis); - static void _draw_half_offset_side_terrain_bit(CanvasItem *p_canvas_item, Color p_color, Vector2i p_size, TileSet::CellNeighbor p_bit, float p_overlap, TileSet::TileOffsetAxis p_offset_axis); - -public: - static void draw_terrains(CanvasItem *p_canvas_item, Transform2D p_transform, TileSet *p_tile_set, const TileData *p_tile_data); -}; - class TileSetPluginAtlasPhysics : public TileSetPlugin { GDCLASS(TileSetPluginAtlasPhysics, TileSetPlugin); -- cgit v1.2.3