1 /*************************************************************************/
2 /* tile_set_editor_plugin.cpp */
3 /*************************************************************************/
4 /* This file is part of: */
5 /* GODOT ENGINE */
6 /* https://godotengine.org */
7 /*************************************************************************/
8 /* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
9 /* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
10 /* */
11 /* Permission is hereby granted, free of charge, to any person obtaining */
12 /* a copy of this software and associated documentation files (the */
13 /* "Software"), to deal in the Software without restriction, including */
14 /* without limitation the rights to use, copy, modify, merge, publish, */
15 /* distribute, sublicense, and/or sell copies of the Software, and to */
16 /* permit persons to whom the Software is furnished to do so, subject to */
17 /* the following conditions: */
18 /* */
19 /* The above copyright notice and this permission notice shall be */
20 /* included in all copies or substantial portions of the Software. */
21 /* */
22 /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23 /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24 /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
25 /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26 /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27 /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29 /*************************************************************************/
30 #include "tile_set_editor_plugin.h"
31 #include "scene/2d/physics_body_2d.h"
32 #include "scene/2d/sprite.h"
edit(const Ref<TileSet> & p_tileset)33 void TileSetEditor::edit(const Ref<TileSet> &p_tileset) {
34
35 tileset = p_tileset;
36 }
37
_import_node(Node * p_node,Ref<TileSet> p_library)38 void TileSetEditor::_import_node(Node *p_node, Ref<TileSet> p_library) {
39
40 for (int i = 0; i < p_node->get_child_count(); i++) {
41
42 Node *child = p_node->get_child(i);
43
44 if (!child->cast_to<Sprite>()) {
45 if (child->get_child_count() > 0) {
46 _import_node(child, p_library);
47 }
48
49 continue;
50 }
51
52 Sprite *mi = child->cast_to<Sprite>();
53 Ref<Texture> texture = mi->get_texture();
54 Ref<CanvasItemMaterial> material = mi->get_material();
55
56 if (texture.is_null())
57 continue;
58
59 int id = p_library->find_tile_by_name(mi->get_name());
60 if (id < 0) {
61
62 id = p_library->get_last_unused_tile_id();
63 p_library->create_tile(id);
64 p_library->tile_set_name(id, mi->get_name());
65 }
66
67 p_library->tile_set_texture(id, texture);
68 p_library->tile_set_material(id, material);
69
70 p_library->tile_set_modulate(id, mi->get_modulate());
71
72 Vector2 phys_offset;
73 Size2 s;
74
75 if (mi->is_region()) {
76 s = mi->get_region_rect().size;
77 p_library->tile_set_region(id, mi->get_region_rect());
78 } else {
79 const int frame = mi->get_frame();
80 const int hframes = mi->get_hframes();
81 s = texture->get_size() / Size2(hframes, mi->get_vframes());
82 p_library->tile_set_region(id, Rect2(Vector2(frame % hframes, frame / hframes) * s, s));
83 }
84
85 if (mi->is_centered()) {
86 phys_offset += -s / 2;
87 }
88
89 Vector<Ref<Shape2D> > collisions;
90 Ref<NavigationPolygon> nav_poly;
91 Ref<OccluderPolygon2D> occluder;
92 bool one_way_ok = true;
93 Variant one_way_dir;
94 float one_way_max_depth = 0.0f;
95
96 for (int j = 0; j < mi->get_child_count(); j++) {
97
98 Node *child2 = mi->get_child(j);
99
100 if (child2->cast_to<NavigationPolygonInstance>())
101 nav_poly = child2->cast_to<NavigationPolygonInstance>()->get_navigation_polygon();
102
103 if (child2->cast_to<LightOccluder2D>())
104 occluder = child2->cast_to<LightOccluder2D>()->get_occluder_polygon();
105
106 if (!child2->cast_to<StaticBody2D>())
107 continue;
108 StaticBody2D *sb = child2->cast_to<StaticBody2D>();
109 int shape_count = sb->get_shape_count();
110 if (shape_count == 0)
111 continue;
112 for (int shape_index = 0; shape_index < shape_count; ++shape_index) {
113 Ref<Shape2D> collision = sb->get_shape(shape_index);
114 if (collision.is_valid()) {
115 collisions.push_back(collision);
116 }
117 }
118
119 phys_offset -= sb->get_pos();
120
121 if (one_way_ok) {
122 Vector2 curr_dir = sb->get_one_way_collision_direction();
123 float curr_max_depth = sb->get_one_way_collision_max_depth();
124 if (one_way_dir == Variant()) {
125 one_way_dir = curr_dir;
126 one_way_max_depth = curr_max_depth;
127 } else {
128 if (curr_dir != one_way_dir || curr_max_depth != one_way_max_depth) {
129 one_way_ok = false;
130 WARN_PRINT(String("Mismatch in one-way collision parameters for " + child->get_name()).utf8().get_data());
131 }
132 }
133 }
134 }
135
136 if (collisions.size()) {
137
138 p_library->tile_set_shapes(id, collisions);
139 p_library->tile_set_shape_offset(id, -phys_offset);
140 if (one_way_ok && one_way_dir != Variant()) {
141 p_library->tile_set_one_way_collision_direction(id, one_way_dir);
142 p_library->tile_set_one_way_collision_max_depth(id, one_way_max_depth);
143 }
144 } else {
145 p_library->tile_set_shape_offset(id, Vector2());
146 }
147
148 p_library->tile_set_texture_offset(id, mi->get_offset());
149 p_library->tile_set_navigation_polygon(id, nav_poly);
150 p_library->tile_set_light_occluder(id, occluder);
151 p_library->tile_set_occluder_offset(id, -phys_offset);
152 p_library->tile_set_navigation_polygon_offset(id, -phys_offset);
153 }
154 }
155
_import_scene(Node * p_scene,Ref<TileSet> p_library,bool p_merge)156 void TileSetEditor::_import_scene(Node *p_scene, Ref<TileSet> p_library, bool p_merge) {
157 if (!p_merge)
158 p_library->clear();
159
160 _import_node(p_scene, p_library);
161 }
162
_menu_confirm()163 void TileSetEditor::_menu_confirm() {
164
165 switch (option) {
166
167 case MENU_OPTION_MERGE_FROM_SCENE:
168 case MENU_OPTION_CREATE_FROM_SCENE: {
169
170 EditorNode *en = editor;
171 Node *scene = en->get_edited_scene();
172 if (!scene)
173 break;
174
175 _import_scene(scene, tileset, option == MENU_OPTION_MERGE_FROM_SCENE);
176
177 } break;
178 }
179 }
180
_name_dialog_confirm(const String & name)181 void TileSetEditor::_name_dialog_confirm(const String &name) {
182
183 switch (option) {
184
185 case MENU_OPTION_REMOVE_ITEM: {
186
187 int id = tileset->find_tile_by_name(name);
188
189 if (id < 0 && name.is_valid_integer())
190 id = name.to_int();
191
192 if (tileset->has_tile(id)) {
193 tileset->remove_tile(id);
194 } else {
195 err_dialog->set_text(TTR("Could not find tile:") + " " + name);
196 err_dialog->popup_centered(Size2(300, 60));
197 }
198 } break;
199 }
200 }
201
_menu_cbk(int p_option)202 void TileSetEditor::_menu_cbk(int p_option) {
203
204 option = p_option;
205 switch (p_option) {
206
207 case MENU_OPTION_ADD_ITEM: {
208
209 tileset->create_tile(tileset->get_last_unused_tile_id());
210 } break;
211 case MENU_OPTION_REMOVE_ITEM: {
212
213 nd->set_title(TTR("Remove Item"));
214 nd->set_text(TTR("Item name or ID:"));
215 nd->popup_centered(Size2(300, 95));
216 } break;
217 case MENU_OPTION_CREATE_FROM_SCENE: {
218
219 cd->set_text(TTR("Create from scene?"));
220 cd->popup_centered(Size2(300, 60));
221 } break;
222 case MENU_OPTION_MERGE_FROM_SCENE: {
223
224 cd->set_text(TTR("Merge from scene?"));
225 cd->popup_centered(Size2(300, 60));
226 } break;
227 }
228 }
229
update_library_file(Node * p_base_scene,Ref<TileSet> ml,bool p_merge)230 Error TileSetEditor::update_library_file(Node *p_base_scene, Ref<TileSet> ml, bool p_merge) {
231
232 _import_scene(p_base_scene, ml, p_merge);
233 return OK;
234 }
235
_bind_methods()236 void TileSetEditor::_bind_methods() {
237
238 ObjectTypeDB::bind_method("_menu_cbk", &TileSetEditor::_menu_cbk);
239 ObjectTypeDB::bind_method("_menu_confirm", &TileSetEditor::_menu_confirm);
240 ObjectTypeDB::bind_method("_name_dialog_confirm", &TileSetEditor::_name_dialog_confirm);
241 }
242
TileSetEditor(EditorNode * p_editor)243 TileSetEditor::TileSetEditor(EditorNode *p_editor) {
244
245 Panel *panel = memnew(Panel);
246 panel->set_area_as_parent_rect();
247 add_child(panel);
248 MenuButton *options = memnew(MenuButton);
249 panel->add_child(options);
250 options->set_pos(Point2(1, 1));
251 options->set_text("Theme");
252 options->get_popup()->add_item(TTR("Add Item"), MENU_OPTION_ADD_ITEM);
253 options->get_popup()->add_item(TTR("Remove Item"), MENU_OPTION_REMOVE_ITEM);
254 options->get_popup()->add_separator();
255 options->get_popup()->add_item(TTR("Create from Scene"), MENU_OPTION_CREATE_FROM_SCENE);
256 options->get_popup()->add_item(TTR("Merge from Scene"), MENU_OPTION_MERGE_FROM_SCENE);
257 options->get_popup()->connect("item_pressed", this, "_menu_cbk");
258 editor = p_editor;
259 cd = memnew(ConfirmationDialog);
260 add_child(cd);
261 cd->get_ok()->connect("pressed", this, "_menu_confirm");
262
263 nd = memnew(EditorNameDialog);
264 add_child(nd);
265 nd->set_hide_on_ok(true);
266 nd->get_line_edit()->set_margin(MARGIN_TOP, 28);
267 nd->connect("name_confirmed", this, "_name_dialog_confirm");
268
269 err_dialog = memnew(AcceptDialog);
270 add_child(err_dialog);
271 err_dialog->set_title(TTR("Error"));
272 }
273
edit(Object * p_node)274 void TileSetEditorPlugin::edit(Object *p_node) {
275
276 if (p_node && p_node->cast_to<TileSet>()) {
277 tileset_editor->edit(p_node->cast_to<TileSet>());
278 tileset_editor->show();
279 } else
280 tileset_editor->hide();
281 }
282
handles(Object * p_node) const283 bool TileSetEditorPlugin::handles(Object *p_node) const {
284
285 return p_node->is_type("TileSet");
286 }
287
make_visible(bool p_visible)288 void TileSetEditorPlugin::make_visible(bool p_visible) {
289
290 if (p_visible)
291 tileset_editor->show();
292 else
293 tileset_editor->hide();
294 }
295
TileSetEditorPlugin(EditorNode * p_node)296 TileSetEditorPlugin::TileSetEditorPlugin(EditorNode *p_node) {
297
298 tileset_editor = memnew(TileSetEditor(p_node));
299
300 p_node->get_viewport()->add_child(tileset_editor);
301 tileset_editor->set_area_as_parent_rect();
302 tileset_editor->set_anchor(MARGIN_RIGHT, Control::ANCHOR_END);
303 tileset_editor->set_anchor(MARGIN_BOTTOM, Control::ANCHOR_BEGIN);
304 tileset_editor->set_end(Point2(0, 22));
305 tileset_editor->hide();
306 }
307