1 | // SuperTux |
2 | // Copyright (C) 2015 Hume2 <teratux.mail@gmail.com> |
3 | // |
4 | // This program is free software: you can redistribute it and/or modify |
5 | // it under the terms of the GNU General Public License as published by |
6 | // the Free Software Foundation, either version 3 of the License, or |
7 | // (at your option) any later version. |
8 | // |
9 | // This program is distributed in the hope that it will be useful, |
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | // GNU General Public License for more details. |
13 | // |
14 | // You should have received a copy of the GNU General Public License |
15 | // along with this program. If not, see <http://www.gnu.org/licenses/>. |
16 | |
17 | #include "editor/overlay_widget.hpp" |
18 | |
19 | #include "util/reader_document.hpp" |
20 | #include "util/reader_mapping.hpp" |
21 | #include "util/writer.hpp" |
22 | |
23 | #include "editor/editor.hpp" |
24 | #include "editor/node_marker.hpp" |
25 | #include "editor/object_menu.hpp" |
26 | #include "editor/object_info.hpp" |
27 | #include "editor/tile_selection.hpp" |
28 | #include "editor/tip.hpp" |
29 | #include "editor/util.hpp" |
30 | #include "editor/worldmap_objects.hpp" |
31 | #include "gui/menu.hpp" |
32 | #include "gui/menu_manager.hpp" |
33 | #include "object/camera.hpp" |
34 | #include "object/path_gameobject.hpp" |
35 | #include "object/tilemap.hpp" |
36 | #include "supertux/game_object_factory.hpp" |
37 | #include "supertux/sector.hpp" |
38 | #include "video/renderer.hpp" |
39 | #include "video/video_system.hpp" |
40 | #include "video/viewport.hpp" |
41 | |
42 | namespace { |
43 | |
44 | const int snap_grid_sizes[4] = {4, 8, 16, 32}; |
45 | |
46 | } // namespace |
47 | |
48 | bool EditorOverlayWidget::render_background = true; |
49 | bool EditorOverlayWidget::render_grid = true; |
50 | bool EditorOverlayWidget::snap_to_grid = true; |
51 | int EditorOverlayWidget::selected_snap_grid_size = 3; |
52 | |
53 | EditorOverlayWidget::EditorOverlayWidget(Editor& editor) : |
54 | m_editor(editor), |
55 | m_hovered_tile(0, 0), |
56 | m_sector_pos(0, 0), |
57 | m_mouse_pos(0, 0), |
58 | m_dragging(false), |
59 | m_dragging_right(false), |
60 | m_drag_start(0, 0), |
61 | m_dragged_object(nullptr), |
62 | m_hovered_object(nullptr), |
63 | m_selected_object(nullptr), |
64 | m_edited_path(nullptr), |
65 | m_last_node_marker(nullptr), |
66 | m_object_tip(), |
67 | m_obj_mouse_desync(0, 0) |
68 | { |
69 | } |
70 | |
71 | EditorOverlayWidget::~EditorOverlayWidget() |
72 | { |
73 | } |
74 | |
75 | void |
76 | EditorOverlayWidget::update(float dt_sec) |
77 | { |
78 | if (m_hovered_object && !m_hovered_object->is_valid()) { |
79 | m_hovered_object = nullptr; |
80 | m_object_tip = nullptr; |
81 | } |
82 | |
83 | if (m_selected_object && !m_selected_object->is_valid()) { |
84 | delete_markers(); |
85 | } |
86 | } |
87 | |
88 | void |
89 | EditorOverlayWidget::on_level_change() |
90 | { |
91 | m_dragged_object = nullptr; |
92 | m_selected_object = nullptr; |
93 | m_edited_path = nullptr; |
94 | m_last_node_marker = nullptr; |
95 | m_hovered_object = nullptr; |
96 | } |
97 | |
98 | void |
99 | EditorOverlayWidget::delete_markers() |
100 | { |
101 | auto* sector = m_editor.get_sector(); |
102 | |
103 | if (m_selected_object) { |
104 | m_selected_object->editor_deselect(); |
105 | } |
106 | |
107 | for (auto& marker : sector->get_objects_by_type<MarkerObject>()) { |
108 | marker.remove_me(); |
109 | } |
110 | |
111 | m_selected_object = nullptr; |
112 | m_edited_path = nullptr; |
113 | m_last_node_marker = nullptr; |
114 | } |
115 | |
116 | Rectf |
117 | EditorOverlayWidget::drag_rect() |
118 | { |
119 | int start_x, start_y, end_x, end_y; |
120 | |
121 | if (m_drag_start.x < m_sector_pos.x) { |
122 | start_x = static_cast<int>(m_drag_start.x); |
123 | end_x = static_cast<int>(m_sector_pos.x); |
124 | } else { |
125 | start_x = static_cast<int>(m_sector_pos.x); |
126 | end_x = static_cast<int>(m_drag_start.x); |
127 | } |
128 | |
129 | if (m_drag_start.y < m_sector_pos.y) { |
130 | start_y = static_cast<int>(m_drag_start.y); |
131 | end_y = static_cast<int>(m_sector_pos.y); |
132 | } else { |
133 | start_y = static_cast<int>(m_sector_pos.y); |
134 | end_y = static_cast<int>(m_drag_start.y); |
135 | } |
136 | |
137 | return Rectf( static_cast<float>(start_x), |
138 | static_cast<float>(start_y), |
139 | static_cast<float>(end_x), |
140 | static_cast<float>(end_y) ); |
141 | } |
142 | |
143 | void |
144 | EditorOverlayWidget::input_tile(const Vector& pos, uint32_t tile) |
145 | { |
146 | auto tilemap = dynamic_cast<TileMap*>(m_editor.get_selected_tilemap()); |
147 | if ( !tilemap ) { |
148 | return; |
149 | } |
150 | |
151 | if ( pos.x < 0 || |
152 | pos.y < 0 || |
153 | pos.x >= static_cast<float>(tilemap->get_width()) || |
154 | pos.y >= static_cast<float>(tilemap->get_height())) { |
155 | return; |
156 | } |
157 | |
158 | tilemap->change(static_cast<int>(pos.x), static_cast<int>(pos.y), tile); |
159 | } |
160 | |
161 | void |
162 | EditorOverlayWidget::put_tile() |
163 | { |
164 | auto tiles = m_editor.get_tiles(); |
165 | Vector add_tile; |
166 | for (add_tile.x = static_cast<float>(tiles->m_width) - 1.0f; add_tile.x >= 0.0f; add_tile.x--) { |
167 | for (add_tile.y = static_cast<float>(tiles->m_height) - 1.0f; add_tile.y >= 0; add_tile.y--) { |
168 | input_tile(m_hovered_tile + add_tile, tiles->pos(static_cast<int>(add_tile.x), |
169 | static_cast<int>(add_tile.y))); |
170 | } |
171 | } |
172 | } |
173 | |
174 | void |
175 | EditorOverlayWidget::draw_rectangle() |
176 | { |
177 | Rectf dr = drag_rect(); |
178 | dr.set_p1(sp_to_tp(dr.p1())); |
179 | dr.set_p2(sp_to_tp(dr.p2())); |
180 | bool sgn_x = m_drag_start.x < m_sector_pos.x; |
181 | bool sgn_y = m_drag_start.y < m_sector_pos.y; |
182 | |
183 | int x_ = sgn_x ? 0 : static_cast<int>(-dr.get_width()); |
184 | for (int x = static_cast<int>(dr.get_left()); x <= static_cast<int>(dr.get_right()); x++, x_++) { |
185 | int y_ = sgn_y ? 0 : static_cast<int>(-dr.get_height()); |
186 | for (int y = static_cast<int>(dr.get_top()); y <= static_cast<int>(dr.get_bottom()); y++, y_++) { |
187 | input_tile( Vector(static_cast<float>(x), static_cast<float>(y)), m_editor.get_tiles()->pos(x_, y_) ); |
188 | } |
189 | } |
190 | } |
191 | |
192 | void |
193 | EditorOverlayWidget::fill() |
194 | { |
195 | auto tiles = m_editor.get_tiles(); |
196 | auto tilemap = dynamic_cast<TileMap*>(m_editor.get_selected_tilemap()); |
197 | if (! tilemap) { |
198 | return; |
199 | } |
200 | |
201 | // The tile that is going to be replaced: |
202 | Uint32 replace_tile = tilemap->get_tile_id(static_cast<int>(m_hovered_tile.x), static_cast<int>(m_hovered_tile.y)); |
203 | |
204 | if (replace_tile == tiles->pos(0, 0)) { |
205 | // Replacing by the same tiles shouldn't do anything. |
206 | return; |
207 | } |
208 | |
209 | std::vector<Vector> pos_stack; |
210 | pos_stack.clear(); |
211 | pos_stack.push_back(m_hovered_tile); |
212 | |
213 | // Passing recursively trough all tiles to be replaced... |
214 | while (pos_stack.size()) { |
215 | |
216 | if (pos_stack.size() > 1000000) { |
217 | log_warning << "More than 1'000'000 tiles in stack to fill, STOP" << std::endl; |
218 | return; |
219 | } |
220 | |
221 | Vector pos = pos_stack[pos_stack.size() - 1]; |
222 | Vector tpos = pos - m_hovered_tile; |
223 | |
224 | // Tests for being inside tilemap: |
225 | if ( pos.x < 0 || |
226 | pos.y < 0 || |
227 | pos.x >= static_cast<float>(tilemap->get_width()) || |
228 | pos.y >= static_cast<float>(tilemap->get_height())) |
229 | { |
230 | pos_stack.pop_back(); |
231 | continue; |
232 | } |
233 | |
234 | input_tile(pos, tiles->pos(static_cast<int>(tpos.x), static_cast<int>(tpos.y))); |
235 | Vector pos_; |
236 | |
237 | // Going left... |
238 | pos_ = pos + Vector(-1, 0); |
239 | if (pos_.x >= 0) { |
240 | if (replace_tile == tilemap->get_tile_id(static_cast<int>(pos_.x), static_cast<int>(pos_.y)) && |
241 | replace_tile != tiles->pos(static_cast<int>(tpos.x - 1), static_cast<int>(tpos.y))) { |
242 | pos_stack.push_back( pos_ ); |
243 | continue; |
244 | } |
245 | } |
246 | |
247 | // Going right... |
248 | pos_ = pos + Vector(1, 0); |
249 | if (pos_.x < static_cast<float>(tilemap->get_width())) { |
250 | if (replace_tile == tilemap->get_tile_id(static_cast<int>(pos_.x), static_cast<int>(pos_.y)) && |
251 | replace_tile != tiles->pos(static_cast<int>(tpos.x + 1), static_cast<int>(tpos.y))) { |
252 | pos_stack.push_back( pos_ ); |
253 | continue; |
254 | } |
255 | } |
256 | |
257 | // Going up... |
258 | pos_ = pos + Vector(0, -1); |
259 | if (pos_.y >= 0) { |
260 | if (replace_tile == tilemap->get_tile_id(static_cast<int>(pos_.x), static_cast<int>(pos_.y))&& |
261 | replace_tile != tiles->pos(static_cast<int>(tpos.x), static_cast<int>(tpos.y - 1))) { |
262 | pos_stack.push_back( pos_ ); |
263 | continue; |
264 | } |
265 | } |
266 | |
267 | // Going down... |
268 | pos_ = pos + Vector(0, 1); |
269 | if (pos_.y < static_cast<float>(tilemap->get_height())) { |
270 | if (replace_tile == tilemap->get_tile_id(static_cast<int>(pos_.x), static_cast<int>(pos_.y)) && |
271 | replace_tile != tiles->pos(static_cast<int>(tpos.x), static_cast<int>(tpos.y + 1))) { |
272 | pos_stack.push_back( pos_ ); |
273 | continue; |
274 | } |
275 | } |
276 | |
277 | // When tiles on each side are already filled or occupied by another tiles, it ends. |
278 | pos_stack.pop_back(); |
279 | } |
280 | } |
281 | |
282 | void |
283 | EditorOverlayWidget::hover_object() |
284 | { |
285 | for (auto& moving_object : m_editor.get_sector()->get_objects_by_type<MovingObject>()) |
286 | { |
287 | Rectf bbox = moving_object.get_bbox(); |
288 | if (bbox.contains(m_sector_pos)) { |
289 | if (&moving_object != m_hovered_object) { |
290 | m_hovered_object = &moving_object; |
291 | if (moving_object.has_settings()) { |
292 | m_object_tip = std::make_unique<Tip>(moving_object); |
293 | } |
294 | } |
295 | return; |
296 | } |
297 | } |
298 | m_object_tip = nullptr; |
299 | m_hovered_object = nullptr; |
300 | } |
301 | |
302 | void |
303 | EditorOverlayWidget::edit_path(Path* path, GameObject* new_marked_object) |
304 | { |
305 | if (!path) return; |
306 | delete_markers(); |
307 | |
308 | if (!path->is_valid()) { |
309 | m_edited_path = nullptr; |
310 | return; |
311 | } |
312 | m_edited_path = path; |
313 | m_edited_path->edit_path(); |
314 | if (new_marked_object) { |
315 | m_selected_object = new_marked_object; |
316 | } |
317 | } |
318 | |
319 | void |
320 | EditorOverlayWidget::select_object() |
321 | { |
322 | delete_markers(); |
323 | if (!m_dragged_object || !m_dragged_object->is_valid()) return; |
324 | |
325 | if (m_dragged_object->has_variable_size()) { |
326 | m_selected_object = m_dragged_object; |
327 | m_dragged_object->editor_select(); |
328 | return; |
329 | } |
330 | |
331 | auto path_obj = dynamic_cast<PathObject*>(m_dragged_object); |
332 | if (path_obj && path_obj->get_path()) |
333 | { |
334 | edit_path(path_obj->get_path(), m_dragged_object); |
335 | } |
336 | } |
337 | |
338 | void |
339 | EditorOverlayWidget::grab_object() |
340 | { |
341 | if (m_hovered_object) { |
342 | if (!m_hovered_object->is_valid()) |
343 | { |
344 | m_hovered_object = nullptr; |
345 | } |
346 | else |
347 | { |
348 | m_dragged_object = m_hovered_object; |
349 | m_obj_mouse_desync = m_sector_pos - m_hovered_object->get_pos(); |
350 | |
351 | auto* pm = dynamic_cast<MarkerObject*>(m_hovered_object); |
352 | if (!pm) { |
353 | select_object(); |
354 | } |
355 | m_last_node_marker = dynamic_cast<NodeMarker*>(pm); |
356 | } |
357 | } |
358 | else |
359 | { |
360 | m_dragged_object = nullptr; |
361 | |
362 | if (m_edited_path && |
363 | m_editor.get_tileselect_object() == "#node" && |
364 | m_edited_path->is_valid()) |
365 | { |
366 | // do nothing |
367 | } else { |
368 | delete_markers(); |
369 | } |
370 | } |
371 | } |
372 | |
373 | void |
374 | EditorOverlayWidget::clone_object() |
375 | { |
376 | if (m_hovered_object && m_hovered_object->is_saveable()) { |
377 | if (!m_hovered_object->is_valid()) { |
378 | m_hovered_object = nullptr; |
379 | return; |
380 | } |
381 | |
382 | auto* pm = dynamic_cast<MarkerObject*>(m_hovered_object); |
383 | if (!pm) |
384 | { |
385 | m_obj_mouse_desync = m_sector_pos - m_hovered_object->get_pos(); |
386 | |
387 | // clone the current object by means of saving and loading it |
388 | auto game_object_uptr = [this]{ |
389 | std::stringstream stream; |
390 | Writer writer(stream); |
391 | writer.start_list(m_hovered_object->get_class()); |
392 | m_hovered_object->save(writer); |
393 | writer.end_list(m_hovered_object->get_class()); |
394 | |
395 | auto doc = ReaderDocument::from_stream(stream); |
396 | auto object_sx = doc.get_root(); |
397 | return GameObjectFactory::instance().create(object_sx.get_name(), object_sx.get_mapping()); |
398 | }(); |
399 | |
400 | GameObject& game_object = m_editor.get_sector()->add_object(std::move(game_object_uptr)); |
401 | |
402 | m_dragged_object = dynamic_cast<MovingObject*>(&game_object); |
403 | m_dragged_object->after_editor_set(); |
404 | } |
405 | } |
406 | else |
407 | { |
408 | m_dragged_object = nullptr; |
409 | delete_markers(); |
410 | } |
411 | } |
412 | |
413 | void |
414 | EditorOverlayWidget::(GameObject& object) |
415 | { |
416 | auto = std::make_unique<ObjectMenu>(m_editor, &object); |
417 | m_editor.m_deactivate_request = true; |
418 | MenuManager::instance().push_menu(std::move(menu)); |
419 | } |
420 | |
421 | void |
422 | EditorOverlayWidget::move_object() |
423 | { |
424 | if (m_dragged_object) { |
425 | if (!m_dragged_object->is_valid()) { |
426 | m_dragged_object = nullptr; |
427 | return; |
428 | } |
429 | Vector new_pos = m_sector_pos - m_obj_mouse_desync; |
430 | if (snap_to_grid) { |
431 | auto& snap_grid_size = snap_grid_sizes[selected_snap_grid_size]; |
432 | new_pos = (new_pos / static_cast<float>(snap_grid_size)).floor() * static_cast<float>(snap_grid_size); |
433 | |
434 | auto pm = dynamic_cast<MarkerObject*>(m_dragged_object); |
435 | if (pm) { |
436 | new_pos -= pm->get_offset(); |
437 | } |
438 | } |
439 | m_dragged_object->move_to(new_pos); |
440 | } |
441 | } |
442 | |
443 | void |
444 | EditorOverlayWidget::rubber_object() |
445 | { |
446 | if (!m_edited_path) { |
447 | delete_markers(); |
448 | } |
449 | if (m_dragged_object) { |
450 | m_dragged_object->editor_delete(); |
451 | } |
452 | m_last_node_marker = nullptr; |
453 | } |
454 | |
455 | void |
456 | EditorOverlayWidget::rubber_rect() |
457 | { |
458 | delete_markers(); |
459 | Rectf dr = drag_rect(); |
460 | for (auto& moving_object : m_editor.get_sector()->get_objects_by_type<MovingObject>()) { |
461 | Rectf bbox = moving_object.get_bbox(); |
462 | if (dr.contains(bbox)) { |
463 | moving_object.editor_delete(); |
464 | } |
465 | } |
466 | m_last_node_marker = nullptr; |
467 | } |
468 | |
469 | void |
470 | EditorOverlayWidget::update_node_iterators() |
471 | { |
472 | if (!m_edited_path) return; |
473 | if (!m_edited_path->is_valid()) return; |
474 | |
475 | auto* sector = m_editor.get_sector(); |
476 | for (auto& moving_object : sector->get_objects_by_type<MovingObject>()) { |
477 | auto marker = dynamic_cast<NodeMarker*>(&moving_object); |
478 | if (marker) { |
479 | marker->update_iterator(); |
480 | } |
481 | } |
482 | } |
483 | |
484 | void |
485 | EditorOverlayWidget::add_path_node() |
486 | { |
487 | Path::Node new_node; |
488 | new_node.position = m_sector_pos; |
489 | new_node.time = 1; |
490 | m_edited_path->m_nodes.insert(m_last_node_marker->m_node + 1, new_node); |
491 | Sector::get().add<NodeMarker>(m_edited_path, m_edited_path->m_nodes.end() - 1, m_edited_path->m_nodes.size() - 1); |
492 | //last_node_marker = dynamic_cast<NodeMarker*>(marker.get()); |
493 | update_node_iterators(); |
494 | m_editor.get_sector()->flush_game_objects(); |
495 | grab_object(); |
496 | } |
497 | |
498 | void |
499 | EditorOverlayWidget::put_object() |
500 | { |
501 | const std::string& object_class = m_editor.get_tileselect_object(); |
502 | if (object_class[0] == '#') |
503 | { |
504 | if (m_edited_path && object_class == "#node" ) { |
505 | if (m_edited_path->is_valid() && m_last_node_marker) { |
506 | add_path_node(); |
507 | } |
508 | } |
509 | } |
510 | else |
511 | { |
512 | auto target_pos = m_sector_pos; |
513 | if (snap_to_grid) |
514 | { |
515 | auto& snap_grid_size = snap_grid_sizes[selected_snap_grid_size]; |
516 | target_pos = (m_sector_pos / static_cast<float>(snap_grid_size)).floor() * static_cast<float>(snap_grid_size); |
517 | } |
518 | |
519 | auto object = GameObjectFactory::instance().create(object_class, target_pos, Direction::LEFT); |
520 | object->after_editor_set(); |
521 | |
522 | auto* mo = dynamic_cast<MovingObject*> (object.get()); |
523 | if (!mo) { |
524 | if (!dynamic_cast<PathGameObject*>(object.get())) { |
525 | m_editor.add_layer(object.get()); |
526 | } |
527 | } else if (!snap_to_grid) { |
528 | auto bbox = mo->get_bbox(); |
529 | mo->move_to(mo->get_pos() - Vector(bbox.get_width() / 2, bbox.get_height() / 2)); |
530 | } |
531 | |
532 | auto* wo = dynamic_cast<worldmap_editor::WorldmapObject*>(object.get()); |
533 | if (wo) { |
534 | wo->move_to(wo->get_pos() / 32); |
535 | } |
536 | |
537 | m_editor.get_sector()->add_object(std::move(object)); |
538 | } |
539 | } |
540 | |
541 | void |
542 | EditorOverlayWidget::process_left_click() |
543 | { |
544 | m_dragging = true; |
545 | m_dragging_right = false; |
546 | m_drag_start = m_sector_pos; |
547 | |
548 | switch (m_editor.get_tileselect_input_type()) |
549 | { |
550 | case EditorToolboxWidget::InputType::TILE: |
551 | switch (m_editor.get_tileselect_select_mode()) |
552 | { |
553 | case 0: |
554 | put_tile(); |
555 | break; |
556 | |
557 | case 1: |
558 | draw_rectangle(); |
559 | break; |
560 | |
561 | case 2: |
562 | fill(); |
563 | break; |
564 | |
565 | default: |
566 | break; |
567 | } |
568 | break; |
569 | |
570 | case EditorToolboxWidget::InputType::OBJECT: |
571 | switch (m_editor.get_tileselect_move_mode()) |
572 | { |
573 | case 0: |
574 | grab_object(); |
575 | break; |
576 | |
577 | case 1: |
578 | clone_object(); |
579 | break; |
580 | |
581 | default: |
582 | break; |
583 | } |
584 | |
585 | if (!m_editor.get_tileselect_object().empty()) { |
586 | if (!m_dragged_object) { |
587 | put_object(); |
588 | } |
589 | } else { |
590 | rubber_object(); |
591 | } |
592 | break; |
593 | |
594 | default: |
595 | break; |
596 | } |
597 | } |
598 | |
599 | void |
600 | EditorOverlayWidget::process_right_click() |
601 | { |
602 | switch (m_editor.get_tileselect_input_type()) |
603 | { |
604 | case EditorToolboxWidget::InputType::TILE: |
605 | m_dragging = true; |
606 | m_dragging_right = true; |
607 | m_drag_start = m_sector_pos; |
608 | update_tile_selection(); |
609 | break; |
610 | |
611 | case EditorToolboxWidget::InputType::NONE: |
612 | case EditorToolboxWidget::InputType::OBJECT: |
613 | { |
614 | if (m_hovered_object && |
615 | m_hovered_object->is_valid() && |
616 | m_hovered_object->has_settings()) |
617 | { |
618 | show_object_menu(*m_hovered_object); |
619 | } |
620 | } |
621 | break; |
622 | |
623 | default: |
624 | break; |
625 | } |
626 | } |
627 | |
628 | Rectf |
629 | EditorOverlayWidget::tile_drag_rect() |
630 | { |
631 | Rectf result = drag_rect(); |
632 | |
633 | // Increase drag rectangle size to the |
634 | // nearest tile border respectively. |
635 | result = Rectf(floorf(result.get_left() / 32) * 32, |
636 | floorf(result.get_top() / 32) * 32, |
637 | ceilf(result.get_right() / 32) * 32, |
638 | ceilf(result.get_bottom() / 32) * 32); |
639 | result.set_p1(sp_to_tp(result.p1())); |
640 | result.set_p2(sp_to_tp(result.p2())); |
641 | return result; |
642 | } |
643 | |
644 | Rectf |
645 | EditorOverlayWidget::selection_draw_rect() |
646 | { |
647 | Rectf select = tile_drag_rect(); |
648 | select.set_p1(tile_screen_pos(select.p1())); |
649 | select.set_p2(tile_screen_pos(select.p2())); |
650 | return select; |
651 | } |
652 | |
653 | void |
654 | EditorOverlayWidget::update_tile_selection() |
655 | { |
656 | Rectf select = tile_drag_rect(); |
657 | auto tiles = m_editor.get_tiles(); |
658 | auto tilemap = dynamic_cast<TileMap*>(m_editor.get_selected_tilemap()); |
659 | if ( !tilemap ) { |
660 | return; |
661 | } |
662 | |
663 | tiles->m_tiles.clear(); |
664 | tiles->m_width = static_cast<int>(select.get_width()); |
665 | tiles->m_height = static_cast<int>(select.get_height()); |
666 | |
667 | int w = static_cast<int>(tilemap->get_width()); |
668 | int h = static_cast<int>(tilemap->get_height()); |
669 | for (int y = static_cast<int>(select.get_top()); y < static_cast<int>(select.get_bottom()); y++) { |
670 | for (int x = static_cast<int>(select.get_left()); x < static_cast<int>(select.get_right()); x++) { |
671 | if ( x < 0 || y < 0 || x >= w || y >= h) { |
672 | tiles->m_tiles.push_back(0); |
673 | } else { |
674 | tiles->m_tiles.push_back(tilemap->get_tile_id(x, y)); |
675 | } |
676 | } |
677 | } |
678 | } |
679 | |
680 | bool |
681 | EditorOverlayWidget::on_mouse_button_up(const SDL_MouseButtonEvent& button) |
682 | { |
683 | m_dragging = false; |
684 | return true; |
685 | } |
686 | |
687 | bool |
688 | EditorOverlayWidget::on_mouse_button_down(const SDL_MouseButtonEvent& button) |
689 | { |
690 | switch (button.button) |
691 | { |
692 | case SDL_BUTTON_LEFT: |
693 | process_left_click(); |
694 | return true; |
695 | |
696 | case SDL_BUTTON_RIGHT: |
697 | process_right_click(); |
698 | return true; |
699 | |
700 | default: |
701 | return false; |
702 | } |
703 | } |
704 | |
705 | bool |
706 | EditorOverlayWidget::on_mouse_motion(const SDL_MouseMotionEvent& motion) |
707 | { |
708 | m_mouse_pos = VideoSystem::current()->get_viewport().to_logical(motion.x, motion.y); |
709 | update_pos(); |
710 | |
711 | if (m_dragging) |
712 | { |
713 | switch (m_editor.get_tileselect_input_type()) |
714 | { |
715 | case EditorToolboxWidget::InputType::TILE: |
716 | if (m_dragging_right) { |
717 | update_tile_selection(); |
718 | } else { |
719 | switch (m_editor.get_tileselect_select_mode()) { |
720 | case 0: |
721 | put_tile(); |
722 | break; |
723 | case 1: |
724 | draw_rectangle(); |
725 | break; |
726 | default: |
727 | break; |
728 | } |
729 | } |
730 | break; |
731 | |
732 | case EditorToolboxWidget::InputType::OBJECT: |
733 | if (m_editor.get_tileselect_object().empty()) { |
734 | if (m_editor.get_tileselect_select_mode() == 1) { |
735 | rubber_rect(); |
736 | } |
737 | } else { |
738 | move_object(); |
739 | } |
740 | break; |
741 | |
742 | default: |
743 | break; |
744 | } |
745 | return true; |
746 | } |
747 | else |
748 | { |
749 | return false; |
750 | } |
751 | } |
752 | |
753 | bool |
754 | EditorOverlayWidget::on_key_up(const SDL_KeyboardEvent& key) |
755 | { |
756 | auto sym = key.keysym.sym; |
757 | if (sym == SDLK_LSHIFT || sym == SDLK_RSHIFT) |
758 | { |
759 | snap_to_grid = !snap_to_grid; |
760 | } |
761 | return true; |
762 | } |
763 | |
764 | bool |
765 | EditorOverlayWidget::on_key_down(const SDL_KeyboardEvent& key) |
766 | { |
767 | auto sym = key.keysym.sym; |
768 | if (sym == SDLK_F8) { |
769 | render_grid = !render_grid; |
770 | } |
771 | if (sym == SDLK_F7 || sym == SDLK_LSHIFT || sym == SDLK_RSHIFT) { |
772 | snap_to_grid = !snap_to_grid; |
773 | } |
774 | return true; |
775 | } |
776 | |
777 | void |
778 | EditorOverlayWidget::update_pos() |
779 | { |
780 | if(m_editor.get_sector() == nullptr) |
781 | return; |
782 | |
783 | m_sector_pos = m_mouse_pos + m_editor.get_sector()->get_camera().get_translation(); |
784 | m_hovered_tile = sp_to_tp(m_sector_pos); |
785 | // update tip |
786 | hover_object(); |
787 | } |
788 | |
789 | void |
790 | EditorOverlayWidget::draw_tile_tip(DrawingContext& context) |
791 | { |
792 | if ( m_editor.get_tileselect_input_type() == EditorToolboxWidget::InputType::TILE ) { |
793 | |
794 | auto tilemap = dynamic_cast<TileMap*>(m_editor.get_selected_tilemap()); |
795 | if (!tilemap) { |
796 | return; |
797 | } |
798 | |
799 | Vector drawn_tile = m_hovered_tile; |
800 | auto tiles = m_editor.get_tiles(); |
801 | |
802 | for (drawn_tile.x = static_cast<float>(tiles->m_width) - 1.0f; drawn_tile.x >= 0.0f; drawn_tile.x--) { |
803 | for (drawn_tile.y = static_cast<float>(tiles->m_height) - 1.0f; drawn_tile.y >= 0.0f; drawn_tile.y--) { |
804 | Vector on_tile = m_hovered_tile + drawn_tile; |
805 | |
806 | if (m_editor.get_tiles()->empty() || |
807 | on_tile.x < 0 || |
808 | on_tile.y < 0 || |
809 | on_tile.x >= static_cast<float>(tilemap->get_width()) || |
810 | on_tile.y >= static_cast<float>(tilemap->get_height())) { |
811 | continue; |
812 | } |
813 | uint32_t tile_id = tiles->pos(static_cast<int>(drawn_tile.x), static_cast<int>(drawn_tile.y)); |
814 | draw_tile(context.color(), *m_editor.get_tileset(), tile_id, |
815 | tp_to_sp(on_tile) - m_editor.get_sector()->get_camera().get_translation(), |
816 | LAYER_GUI-11, Color(1, 1, 1, 0.5)); |
817 | /*if (tile_id) { |
818 | const Tile* tg_tile = m_editor.get_tileset()->get( tile_id ); |
819 | tg_tile->draw(context.color(), tp_to_sp(on_tile) - m_editor.get_sector()->camera->get_translation(), |
820 | LAYER_GUI-11, Color(1, 1, 1, 0.5)); |
821 | }*/ |
822 | } |
823 | } |
824 | } |
825 | } |
826 | |
827 | void |
828 | EditorOverlayWidget::draw_tile_grid(DrawingContext& context, const Color& line_color, int tile_size) |
829 | { |
830 | if ( !m_editor.get_selected_tilemap() ) { |
831 | return; |
832 | } |
833 | |
834 | auto current_tm = dynamic_cast<TileMap*>(m_editor.get_selected_tilemap()); |
835 | if ( current_tm == nullptr ) |
836 | return; |
837 | int tm_width = current_tm->get_width() * (32 / tile_size); |
838 | int tm_height = current_tm->get_height() * (32 / tile_size); |
839 | auto cam_translation = m_editor.get_sector()->get_camera().get_translation(); |
840 | Rectf draw_rect = Rectf(cam_translation, cam_translation + |
841 | Vector(static_cast<float>(context.get_width() - 128), |
842 | static_cast<float>(context.get_height() - 32))); |
843 | Vector start = sp_to_tp( Vector(draw_rect.get_left(), draw_rect.get_top()), tile_size ); |
844 | Vector end = sp_to_tp( Vector(draw_rect.get_right(), draw_rect.get_bottom()), tile_size ); |
845 | start.x = std::max(0.0f, start.x); |
846 | start.y = std::max(0.0f, start.y); |
847 | end.x = std::min(float(tm_width), end.x); |
848 | end.y = std::min(float(tm_height), end.y); |
849 | |
850 | Vector line_start, line_end; |
851 | for (int i = static_cast<int>(start.x); i <= static_cast<int>(end.x); i++) { |
852 | line_start = tile_screen_pos( Vector(static_cast<float>(i), 0.0f), tile_size ); |
853 | line_end = tile_screen_pos( Vector(static_cast<float>(i), end.y), tile_size ); |
854 | context.color().draw_line(line_start, line_end, line_color, current_tm->get_layer()); |
855 | } |
856 | |
857 | for (int i = static_cast<int>(start.y); i <= static_cast<int>(end.y); i++) { |
858 | line_start = tile_screen_pos( Vector(0.0f, static_cast<float>(i)), tile_size ); |
859 | line_end = tile_screen_pos( Vector(end.x, static_cast<float>(i)), tile_size ); |
860 | context.color().draw_line(line_start, line_end, line_color, current_tm->get_layer()); |
861 | } |
862 | } |
863 | |
864 | void |
865 | EditorOverlayWidget::draw_tilemap_border(DrawingContext& context) |
866 | { |
867 | if ( !m_editor.get_selected_tilemap() ) return; |
868 | |
869 | auto current_tm = dynamic_cast<TileMap*>(m_editor.get_selected_tilemap()); |
870 | if ( !current_tm ) return; |
871 | |
872 | Vector start = tile_screen_pos( Vector(0, 0) ); |
873 | Vector end = tile_screen_pos( Vector(static_cast<float>(current_tm->get_width()), |
874 | static_cast<float>(current_tm->get_height())) ); |
875 | context.color().draw_line(start, Vector(start.x, end.y), Color(1, 0, 1), current_tm->get_layer()); |
876 | context.color().draw_line(start, Vector(end.x, start.y), Color(1, 0, 1), current_tm->get_layer()); |
877 | context.color().draw_line(Vector(start.x, end.y), end, Color(1, 0, 1), current_tm->get_layer()); |
878 | context.color().draw_line(Vector(end.x, start.y), end, Color(1, 0, 1), current_tm->get_layer()); |
879 | } |
880 | |
881 | void |
882 | EditorOverlayWidget::draw_path(DrawingContext& context) |
883 | { |
884 | if (!m_edited_path) return; |
885 | if (!m_selected_object) return; |
886 | if (!m_selected_object->is_valid()) return; |
887 | if (!m_edited_path->is_valid()) return; |
888 | |
889 | for (auto i = m_edited_path->m_nodes.begin(); i != m_edited_path->m_nodes.end(); ++i) { |
890 | auto j = i+1; |
891 | Path::Node* node1 = &(*i); |
892 | Path::Node* node2; |
893 | if (j == m_edited_path->m_nodes.end()) { |
894 | if (m_edited_path->m_mode == WalkMode::CIRCULAR) { |
895 | //loop to the first node |
896 | node2 = &(*m_edited_path->m_nodes.begin()); |
897 | } else { |
898 | continue; |
899 | } |
900 | } else { |
901 | node2 = &(*j); |
902 | } |
903 | auto cam_translation = m_editor.get_sector()->get_camera().get_translation(); |
904 | context.color().draw_line(node1->position - cam_translation, |
905 | node2->position - cam_translation, |
906 | Color(1, 0, 0), LAYER_GUI - 21); |
907 | } |
908 | } |
909 | |
910 | void |
911 | EditorOverlayWidget::draw(DrawingContext& context) |
912 | { |
913 | draw_tile_tip(context); |
914 | draw_path(context); |
915 | |
916 | if (render_grid) { |
917 | draw_tile_grid(context, Color(1.f, 1.f, 1.f, 0.2f)); |
918 | draw_tilemap_border(context); |
919 | auto snap_grid_size = snap_grid_sizes[selected_snap_grid_size]; |
920 | if (snap_grid_size != 32) { |
921 | draw_tile_grid(context, Color(1.f, 1.f, 1.f, 0.2f), snap_grid_size); |
922 | } |
923 | } |
924 | |
925 | if (m_object_tip) { |
926 | m_object_tip->draw(context, m_mouse_pos); |
927 | } |
928 | |
929 | if (m_dragging && m_editor.get_tileselect_select_mode() == 1 |
930 | && !m_dragging_right) { |
931 | // Draw selection rectangle... |
932 | auto cam_translation = m_editor.get_sector()->get_camera().get_translation(); |
933 | Vector p0 = m_drag_start - cam_translation; |
934 | Vector p1 = Vector(m_drag_start.x, m_sector_pos.y) - cam_translation; |
935 | Vector p2 = Vector(m_sector_pos.x, m_drag_start.y) - cam_translation; |
936 | |
937 | context.color().draw_filled_rect(Rectf(p0, p1 + Vector(2, 2)), |
938 | Color(0.0f, 1.0f, 0.0f, 1.0f), 0.0f, LAYER_GUI-5); |
939 | context.color().draw_filled_rect(Rectf(p2, m_mouse_pos + Vector(2, 2)), |
940 | Color(0.0f, 1.0f, 0.0f, 1.0f), 0.0f, LAYER_GUI-5); |
941 | context.color().draw_filled_rect(Rectf(p0, p2 + Vector(2, 2)), |
942 | Color(0.0f, 1.0f, 0.0f, 1.0f), 0.0f, LAYER_GUI-5); |
943 | context.color().draw_filled_rect(Rectf(p1, m_mouse_pos + Vector(2, 2)), |
944 | Color(0.0f, 1.0f, 0.0f, 1.0f), 0.0f, LAYER_GUI-5); |
945 | |
946 | context.color().draw_filled_rect(Rectf(p0, m_mouse_pos), |
947 | Color(0.0f, 1.0f, 0.0f, 0.2f), 0.0f, LAYER_GUI-5); |
948 | } |
949 | |
950 | if (m_dragging && m_dragging_right) { |
951 | context.color().draw_filled_rect(selection_draw_rect(), |
952 | Color(0.2f, 0.4f, 1.0f, 0.6f), 0.0f, LAYER_GUI-13); |
953 | } |
954 | } |
955 | |
956 | Vector |
957 | EditorOverlayWidget::tp_to_sp(const Vector& tp, int tile_size) |
958 | { |
959 | auto tilemap = dynamic_cast<TileMap*>(m_editor.get_selected_tilemap()); |
960 | if (!tilemap) |
961 | { |
962 | return Vector(0, 0); |
963 | } |
964 | |
965 | Vector sp = tp * static_cast<float>(tile_size); |
966 | return sp + tilemap->get_offset(); |
967 | } |
968 | |
969 | Vector |
970 | EditorOverlayWidget::sp_to_tp(const Vector& sp, int tile_size) |
971 | { |
972 | auto tilemap = dynamic_cast<TileMap*>(m_editor.get_selected_tilemap()); |
973 | if (!tilemap) |
974 | { |
975 | return Vector(0, 0); |
976 | } |
977 | |
978 | Vector sp_ = sp - tilemap->get_offset(); |
979 | return sp_ / static_cast<float>(tile_size); |
980 | } |
981 | |
982 | Vector |
983 | EditorOverlayWidget::tile_screen_pos(const Vector& tp, int tile_size) |
984 | { |
985 | Vector sp = tp_to_sp(tp, tile_size); |
986 | return sp - m_editor.get_sector()->get_camera().get_translation(); |
987 | } |
988 | |
989 | /* EOF */ |
990 | |