1 #ifndef NO_EDITOR
2 #include "graphics.hpp"
3 #include <iostream>
4 #include <cmath>
5 #include <algorithm>
6
7 #include <boost/bind.hpp>
8 #include <boost/shared_ptr.hpp>
9 #include <boost/algorithm/string.hpp>
10
11 #include "asserts.hpp"
12 #include "border_widget.hpp"
13 #include "button.hpp"
14 #include "character_editor_dialog.hpp"
15 #include "code_editor_dialog.hpp"
16 #include "collision_utils.hpp"
17 #include "color_utils.hpp"
18 #include "custom_object_dialog.hpp"
19 #include "draw_scene.hpp"
20 #include "draw_tile.hpp"
21 #include "debug_console.hpp"
22 #include "editor.hpp"
23 #include "editor_dialogs.hpp"
24 #include "editor_formula_functions.hpp"
25 #include "editor_layers_dialog.hpp"
26 #include "editor_level_properties_dialog.hpp"
27 #include "editor_module_properties_dialog.hpp"
28 #include "editor_stats_dialog.hpp"
29 #include "entity.hpp"
30 #include "filesystem.hpp"
31 #include "font.hpp"
32 #include "foreach.hpp"
33 #include "formatter.hpp"
34 #include "formula.hpp"
35 #include "frame.hpp"
36 #include "grid_widget.hpp"
37 #include "hex_tile.hpp"
38 #include "hex_object.hpp"
39 #include "image_widget.hpp"
40 #include "json_parser.hpp"
41 #include "key.hpp"
42 #include "label.hpp"
43 #include "level.hpp"
44 #include "level_object.hpp"
45 #include "level_runner.hpp"
46 #include "load_level.hpp"
47 #include "module.hpp"
48 #include "multiplayer.hpp"
49 #include "object_events.hpp"
50 #include "player_info.hpp"
51 #include "preferences.hpp"
52 #include "property_editor_dialog.hpp"
53 #include "raster.hpp"
54 #include "segment_editor_dialog.hpp"
55 #include "stats.hpp"
56 #include "texture.hpp"
57 #include "text_editor_widget.hpp"
58 #include "tile_map.hpp"
59 #include "tileset_editor_dialog.hpp"
60 #include "hex_tileset_editor_dialog.hpp"
61 #include "tooltip.hpp"
62 #include "variant.hpp"
63 #include "variant_utils.hpp"
64
65 #include "IMG_savepng.h"
66
67 namespace {
68
69 //keep a map of editors so that when we edit a level and then later
70 //come back to it we'll save all the state we had previously
71 std::map<std::string, editor*> all_editors;
72
73 //the last level we edited
g_last_edited_level()74 std::string& g_last_edited_level() {
75 static std::string str;
76 return str;
77 }
78
79 bool g_draw_stats = false;
80
toggle_draw_stats()81 void toggle_draw_stats() {
82 g_draw_stats = !g_draw_stats;
83 }
84
85 bool g_draw_grid = true;
86
toggle_draw_grid()87 void toggle_draw_grid() {
88 g_draw_grid = !g_draw_grid;
89 }
90 }
91
92 class editor_menu_dialog : public gui::dialog
93 {
94 struct menu_item {
95 std::string description;
96 std::string hotkey;
97 boost::function<void()> action;
98 };
99
execute_menu_item(const std::vector<menu_item> & items,int n)100 void execute_menu_item(const std::vector<menu_item>& items, int n) {
101 if(n >= 0 && n < items.size()) {
102 items[n].action();
103 }
104
105 remove_widget(context_menu_);
106 context_menu_.reset();
107 }
108
show_file_menu()109 void show_file_menu() {
110 menu_item items[] = {
111 "New...", "", boost::bind(&editor_menu_dialog::new_level, this),
112 "Open...", "ctrl+o", boost::bind(&editor_menu_dialog::open_level, this),
113 "Save", "ctrl+s", boost::bind(&editor::save_level, &editor_),
114 "Save As...", "", boost::bind(&editor_menu_dialog::save_level_as, this),
115 "Create New Module...", "", boost::bind(&editor::create_new_module, &editor_),
116 "Edit Module Properties...", "", boost::bind(&editor::edit_module_properties, &editor_),
117 "Create New Object...", "", boost::bind(&editor::create_new_object, &editor_),
118 "Exit", "<esc>", boost::bind(&editor::quit, &editor_),
119 };
120
121 std::vector<menu_item> res;
122 foreach(const menu_item& m, items) {
123 res.push_back(m);
124 }
125 show_menu(res);
126 }
127
show_edit_menu()128 void show_edit_menu() {
129 menu_item items[] = {
130 "Level Properties", "", boost::bind(&editor::edit_level_properties, &editor_),
131 "Undo", "u", boost::bind(&editor::undo_command, &editor_),
132 "Redo", "r", boost::bind(&editor::redo_command, &editor_),
133 "Restart Level", "ctrl+r", boost::bind(&editor::reset_playing_level, &editor_, true),
134 "Restart Level (including player)", "ctrl+alt+r", boost::bind(&editor::reset_playing_level, &editor_, false),
135 "Pause Game", "ctrl+p", boost::bind(&editor::toggle_pause, &editor_),
136 "Code", "", boost::bind(&editor::toggle_code, &editor_),
137 #if defined(USE_GLES2)
138 "Shaders", "", boost::bind(&editor::edit_shaders, &editor_),
139 #endif
140 };
141
142 menu_item duplicate_item = { "Duplicate Object(s)", "ctrl+1", boost::bind(&editor::duplicate_selected_objects, &editor_) };
143
144 std::vector<menu_item> res;
145 foreach(const menu_item& m, items) {
146 res.push_back(m);
147 }
148
149 if(editor_.get_level().editor_selection().empty() == false) {
150 res.push_back(duplicate_item);
151 }
152
153 show_menu(res);
154 }
155
show_view_menu()156 void show_view_menu() {
157 menu_item items[] = {
158 "Zoom Out", "x", boost::bind(&editor::zoom_out, &editor_),
159 "Zoom In", "z", boost::bind(&editor::zoom_in, &editor_),
160 editor_.get_level().show_foreground() ? "Hide Foreground" : "Show Foreground", "f", boost::bind(&level::set_show_foreground, &editor_.get_level(), !editor_.get_level().show_foreground()),
161 editor_.get_level().show_background() ? "Hide Background" : "Show Background", "b", boost::bind(&level::set_show_background, &editor_.get_level(), !editor_.get_level().show_background()),
162 g_draw_stats ? "Hide Stats" : "Show Stats", "", toggle_draw_stats,
163 g_draw_grid ? "Hide Grid" : "Show Grid", "", toggle_draw_grid,
164 preferences::show_debug_hitboxes() ? "Hide Hit Boxes" : "Show Hit Boxes", "h", preferences::toogle_debug_hitboxes,
165 };
166
167 std::vector<menu_item> res;
168 foreach(const menu_item& m, items) {
169 res.push_back(m);
170 }
171 show_menu(res);
172 }
173
show_stats_menu()174 void show_stats_menu() {
175 menu_item items[] = {
176 "Details...", "", boost::bind(&editor::show_stats, &editor_),
177 "Refresh stats", "", boost::bind(&editor::download_stats, &editor_),
178 };
179 std::vector<menu_item> res;
180 foreach(const menu_item& m, items) {
181 res.push_back(m);
182 }
183 show_menu(res);
184 }
185
show_scripts_menu()186 void show_scripts_menu() {
187 std::vector<menu_item> res;
188 foreach(const editor_script::info& script, editor_script::all_scripts()) {
189 menu_item item = { script.name, "", boost::bind(&editor::run_script, &editor_, script.name) };
190 res.push_back(item);
191 }
192
193 show_menu(res);
194 }
195
show_window_menu()196 void show_window_menu() {
197 std::vector<menu_item> res;
198 for(std::map<std::string, editor*>::const_iterator i = all_editors.begin(); i != all_editors.end(); ++i) {
199 std::string name = i->first;
200 if(name == g_last_edited_level()) {
201 name += " *";
202 }
203 menu_item item = { name, "", boost::bind(&editor_menu_dialog::open_level_in_editor, this, i->first) };
204 res.push_back(item);
205 }
206 show_menu(res);
207 }
208
show_menu(const std::vector<menu_item> & items)209 void show_menu(const std::vector<menu_item>& items) {
210 using namespace gui;
211 gui::grid* grid = new gui::grid(2);
212 grid->set_hpad(40);
213 grid->set_show_background(true);
214 grid->allow_selection();
215 grid->swallow_clicks();
216 grid->register_selection_callback(boost::bind(&editor_menu_dialog::execute_menu_item, this, items, _1));
217 foreach(const menu_item& item, items) {
218 grid->add_col(widget_ptr(new label(item.description, graphics::color_white()))).
219 add_col(widget_ptr(new label(item.hotkey, graphics::color_white())));
220 }
221
222 int mousex, mousey;
223 SDL_GetMouseState(&mousex, &mousey);
224
225 mousex -= x();
226 mousey -= y();
227
228 remove_widget(context_menu_);
229 context_menu_.reset(grid);
230 add_widget(context_menu_, mousex, mousey);
231 }
232
233 editor& editor_;
234 gui::widget_ptr context_menu_;
235 gui::button_ptr code_button_;
236 std::string code_button_text_;
237 public:
editor_menu_dialog(editor & e)238 explicit editor_menu_dialog(editor& e)
239 : gui::dialog(0, 0, e.xres() ? e.xres() : 1200, EDITOR_MENUBAR_HEIGHT), editor_(e)
240 {
241 set_clear_bg_amount(255);
242 init();
243 }
244
init()245 void init() {
246 clear();
247
248 using namespace gui;
249 gui::grid* grid = new gui::grid(6);
250 grid->add_col(widget_ptr(
251 new button(widget_ptr(new label("File", graphics::color_white())),
252 boost::bind(&editor_menu_dialog::show_file_menu, this))));
253 grid->add_col(widget_ptr(
254 new button(widget_ptr(new label("Edit", graphics::color_white())),
255 boost::bind(&editor_menu_dialog::show_edit_menu, this))));
256 grid->add_col(widget_ptr(
257 new button(widget_ptr(new label("View", graphics::color_white())),
258 boost::bind(&editor_menu_dialog::show_view_menu, this))));
259 grid->add_col(widget_ptr(
260 new button(widget_ptr(new label("Window", graphics::color_white())),
261 boost::bind(&editor_menu_dialog::show_window_menu, this))));
262 grid->add_col(widget_ptr(
263 new button(widget_ptr(new label("Statistics", graphics::color_white())),
264 boost::bind(&editor_menu_dialog::show_stats_menu, this))));
265 add_widget(widget_ptr(grid));
266
267 grid->add_col(widget_ptr(
268 new button(widget_ptr(new label("Scripts", graphics::color_white())),
269 boost::bind(&editor_menu_dialog::show_scripts_menu, this))));
270 add_widget(widget_ptr(grid));
271
272 code_button_text_ = "";
273 set_code_button_text("Code ->");
274 }
275
set_code_button_text(const std::string & text)276 void set_code_button_text(const std::string& text)
277 {
278 using namespace gui;
279
280 if(code_button_text_ == text) {
281 return;
282 }
283
284 code_button_text_ = text;
285
286 if(code_button_) {
287 remove_widget(code_button_);
288 }
289
290 if(text.empty()) {
291 return;
292 }
293
294 code_button_ = button_ptr(new button(text, boost::bind(&editor::toggle_code, &editor_)));
295
296 add_widget(code_button_, (editor_.xres() ? editor_.xres() : 1200) - 612, 4);
297 }
298
299
new_level()300 void new_level() {
301 using namespace gui;
302 dialog d(0, 0, graphics::screen_width(), graphics::screen_height());
303 d.add_widget(widget_ptr(new label("New Level", graphics::color_white(), 48)));
304 text_editor_widget* entry = new text_editor_widget(200);
305 entry->set_on_enter_handler(boost::bind(&dialog::close, &d));
306 d.add_widget(widget_ptr(new label("Filename:", graphics::color_white())))
307 .add_widget(widget_ptr(entry));
308 d.show_modal();
309
310 std::string name = entry->text();
311 if(name.empty() == false) {
312 variant empty_lvl = json::parse_from_file("data/level/empty.cfg");
313 std::string id = module::make_module_id(name);
314 empty_lvl.add_attr(variant("id"), variant(module::get_id(id)));
315 if(preferences::is_level_path_set()) {
316 sys::write_file(preferences::level_path() + name, empty_lvl.write_json());
317 } else {
318 std::string nn = module::get_id(name);
319 std::string modname = module::get_module_id(name);
320 sys::write_file(module::get_module_path(modname, (preferences::editor_save_to_user_preferences() ? module::BASE_PATH_USER : module::BASE_PATH_GAME)) + preferences::level_path() + nn, empty_lvl.write_json());
321 }
322 loadlevel::load_level_paths();
323 editor_.close();
324 g_last_edited_level() = id;
325 }
326 }
327
save_level_as()328 void save_level_as() {
329 using namespace gui;
330 dialog d(0, 0, graphics::screen_width(), graphics::screen_height());
331 d.add_widget(widget_ptr(new label("Save As", graphics::color_white(), 48)));
332 text_editor_widget* entry = new text_editor_widget(200);
333 entry->set_on_enter_handler(boost::bind(&dialog::close, &d));
334 d.add_widget(widget_ptr(new label("Name:", graphics::color_white())))
335 .add_widget(widget_ptr(entry));
336 d.show_modal();
337
338 if(!d.cancelled() && entry->text().empty() == false) {
339 editor_.save_level_as(entry->text());
340 }
341 }
342
open_level()343 void open_level() {
344 open_level_in_editor(show_choose_level_dialog("Open Level"));
345 }
346
open_level_in_editor(const std::string & lvl)347 void open_level_in_editor(const std::string& lvl) {
348 if(lvl.empty() == false && lvl != g_last_edited_level()) {
349 remove_widget(context_menu_);
350 context_menu_.reset();
351 editor_.close();
352 g_last_edited_level() = lvl;
353 }
354 }
355
356 };
357
358 namespace {
359 const char* ModeStrings[] = {"Tiles", "Objects", "Properties",};
360
361 const char* ToolStrings[] = {
362 "Add tiles by drawing rectangles",
363 "Select Tiles",
364 "Select connected regions of tiles",
365 "Add tiles by drawing pencil strokes",
366 "Pick tiles or objects",
367 "Add Objects",
368 "Select Objects",
369 "Edit Level Segments",
370 "Draw Hexagonal Tiles",
371 };
372
373 const char* ToolIcons[] = {"editor_draw_rect", "editor_rect_select", "editor_wand", "editor_pencil", "editor_eyedropper", "editor_add_object", "editor_select_object", "editor_rect_select", "editor_draw_hexes"};
374 }
375
376 class editor_mode_dialog : public gui::dialog
377 {
378 editor& editor_;
379 gui::widget_ptr context_menu_;
380
381 std::vector<gui::border_widget*> tool_borders_;
382
select_tool(int tool)383 void select_tool(int tool)
384 {
385 if(tool >= 0 && tool < editor::NUM_TOOLS) {
386 editor_.change_tool(static_cast<editor::EDIT_TOOL>(tool));
387 }
388 }
389
handle_event(const SDL_Event & event,bool claimed)390 bool handle_event(const SDL_Event& event, bool claimed)
391 {
392 if(!claimed) {
393 const bool ctrl_pressed = (SDL_GetModState()&(KMOD_LCTRL|KMOD_RCTRL)) != 0;
394 if(ctrl_pressed) {
395 return false;
396 }
397
398 switch(event.type) {
399 case SDL_KEYDOWN: {
400 switch(event.key.keysym.sym) {
401 //TODO: add short cuts for tools here.
402 default:
403 break;
404 }
405
406 break;
407 }
408 }
409 }
410
411 return claimed || dialog::handle_event(event, claimed);
412 }
413
414 public:
editor_mode_dialog(editor & e)415 explicit editor_mode_dialog(editor& e)
416 : gui::dialog(graphics::screen_width() - EDITOR_SIDEBAR_WIDTH, 0, EDITOR_SIDEBAR_WIDTH, 160), editor_(e)
417 {
418 set_clear_bg_amount(255);
419 init();
420 }
421
init()422 void init()
423 {
424 using namespace gui;
425 clear();
426
427 tool_borders_.clear();
428
429 grid_ptr grid(new gui::grid(3));
430 for(int n = 0; n < editor::NUM_TOOLS; ++n) {
431 if(n == editor::TOOL_EDIT_SEGMENTS && editor_.get_level().segment_width() == 0 && editor_.get_level().segment_height() == 0) {
432 continue;
433 }
434 std::cerr << "editor add tool: " << n << std::endl;
435 button_ptr tool_button(
436 new button(widget_ptr(new gui_section_widget(ToolIcons[n], 26, 26)),
437 boost::bind(&editor_mode_dialog::select_tool, this, n)));
438 tool_button->set_tooltip(ToolStrings[n]);
439 tool_borders_.push_back(new border_widget(tool_button, graphics::color(0,0,0,0)));
440 grid->add_col(widget_ptr(tool_borders_.back()));
441 }
442
443 grid->finish_row();
444 add_widget(grid, 5, 5);
445
446 refresh_selection();
447 }
448
refresh_selection()449 void refresh_selection() {
450 using namespace gui;
451 for(int n = 0; n != tool_borders_.size(); ++n) {
452 tool_borders_[n]->set_color(n == editor_.tool() ? graphics::color(255,255,255,255) : graphics::color(0,0,0,0));
453 }
454 }
455 };
456
457 namespace {
458
459 const int RectEdgeSelectThreshold = 6;
460
execute_functions(const std::vector<boost::function<void ()>> & v)461 void execute_functions(const std::vector<boost::function<void()> >& v) {
462 foreach(const boost::function<void()>& f, v) {
463 f();
464 }
465 }
466
467 bool g_started_dragging_object = false;
468
469 //the current state of the rectangle we're dragging
470 rect g_rect_drawing;
471
472 //the tiles that we've drawn in the current action.
473 std::vector<point> g_current_draw_tiles;
474 std::vector<point> g_current_draw_hex_tiles;
475
476 const editor_variable_info* g_variable_editing = NULL;
477 int g_variable_editing_index = -1;
478 variant g_variable_editing_original_value;
variable_info_selected(const_entity_ptr e,int xpos,int ypos,int zoom,int * index_selected=NULL)479 const editor_variable_info* variable_info_selected(const_entity_ptr e, int xpos, int ypos, int zoom, int* index_selected=NULL)
480 {
481 if(index_selected) {
482 *index_selected = -1;
483 }
484
485 if(!e || !e->editor_info()) {
486 return NULL;
487 }
488
489 foreach(const editor_variable_info& var, e->editor_info()->vars()) {
490 const variant value = e->query_value(var.variable_name());
491 switch(var.type()) {
492 case editor_variable_info::XPOSITION: {
493 if(!value.is_int()) {
494 break;
495 }
496
497 if(xpos >= value.as_int() - zoom*RectEdgeSelectThreshold && xpos <= value.as_int() + zoom*RectEdgeSelectThreshold) {
498 return &var;
499 }
500 break;
501 }
502 case editor_variable_info::YPOSITION: {
503 if(!value.is_int()) {
504 break;
505 }
506
507 if(ypos >= value.as_int() - zoom*RectEdgeSelectThreshold && ypos <= value.as_int() + zoom*RectEdgeSelectThreshold) {
508 return &var;
509 }
510 break;
511 }
512 case editor_variable_info::TYPE_POINTS: {
513 if(!value.is_list()) {
514 break;
515 }
516
517 int index = 0;
518 foreach(variant p, value.as_list()) {
519 point pt(p);
520 if(point_in_rect(point(xpos, ypos), rect(pt.x-10, pt.y-10, 20, 20))) {
521 if(index_selected) {
522 *index_selected = index;
523 }
524 return &var;
525 }
526
527 ++index;
528 }
529 }
530 default:
531 break;
532 }
533 }
534
535 return NULL;
536 }
537
round_tile_size(int n)538 int round_tile_size(int n)
539 {
540 if(n >= 0) {
541 return n - n%TileSize;
542 } else {
543 n = -n + 32;
544 return -(n - n%TileSize);
545 }
546 }
547
548 bool resizing_left_level_edge = false,
549 resizing_right_level_edge = false,
550 resizing_top_level_edge = false,
551 resizing_bottom_level_edge = false;
552
modify_selected_rect(const editor & e,rect boundaries,int xpos,int ypos)553 rect modify_selected_rect(const editor& e, rect boundaries, int xpos, int ypos) {
554
555 const int x = round_tile_size(xpos);
556 const int y = round_tile_size(ypos);
557
558 if(resizing_left_level_edge) {
559 boundaries = rect(x, boundaries.y(), boundaries.w() + (boundaries.x() - x), boundaries.h());
560 if(e.get_level().segment_width() > 0) {
561 while(boundaries.w()%e.get_level().segment_width() != 0) {
562 boundaries = rect(boundaries.x()-1, boundaries.y(), boundaries.w()+1, boundaries.h());
563 }
564 }
565 }
566
567 if(resizing_right_level_edge) {
568 boundaries = rect(boundaries.x(), boundaries.y(), x - boundaries.x(), boundaries.h());
569 if(e.get_level().segment_width() > 0) {
570 while(boundaries.w()%e.get_level().segment_width() != 0) {
571 boundaries = rect(boundaries.x(), boundaries.y(), boundaries.w()+1, boundaries.h());
572 }
573 }
574 }
575
576 if(resizing_top_level_edge) {
577 boundaries = rect(boundaries.x(), y, boundaries.w(), boundaries.h() + (boundaries.y() - y));
578 if(e.get_level().segment_height() > 0) {
579 while(boundaries.h()%e.get_level().segment_height() != 0) {
580 boundaries = rect(boundaries.x(), boundaries.y()-1, boundaries.w(), boundaries.h()+1);
581 }
582 }
583 }
584
585 if(resizing_bottom_level_edge) {
586 boundaries = rect(boundaries.x(), boundaries.y(), boundaries.w(), y - boundaries.y());
587 if(e.get_level().segment_height() > 0) {
588 while(boundaries.h()%e.get_level().segment_height() != 0) {
589 boundaries = rect(boundaries.x(), boundaries.y(), boundaries.w(), boundaries.h()+1);
590 }
591 }
592 }
593
594 return boundaries;
595 }
596
597 //find if an edge of a rectangle is selected
rect_left_edge_selected(const rect & r,int x,int y,int zoom)598 bool rect_left_edge_selected(const rect& r, int x, int y, int zoom) {
599 return y >= r.y() - RectEdgeSelectThreshold*zoom &&
600 y <= r.y2() + RectEdgeSelectThreshold*zoom &&
601 x >= r.x() - RectEdgeSelectThreshold*zoom &&
602 x <= r.x() + RectEdgeSelectThreshold*zoom;
603 }
604
rect_right_edge_selected(const rect & r,int x,int y,int zoom)605 bool rect_right_edge_selected(const rect& r, int x, int y, int zoom) {
606 return y >= r.y() - RectEdgeSelectThreshold*zoom &&
607 y <= r.y2() + RectEdgeSelectThreshold*zoom &&
608 x >= r.x2() - RectEdgeSelectThreshold*zoom &&
609 x <= r.x2() + RectEdgeSelectThreshold*zoom;
610 }
611
rect_top_edge_selected(const rect & r,int x,int y,int zoom)612 bool rect_top_edge_selected(const rect& r, int x, int y, int zoom) {
613 return x >= r.x() - RectEdgeSelectThreshold*zoom &&
614 x <= r.x2() + RectEdgeSelectThreshold*zoom &&
615 y >= r.y() - RectEdgeSelectThreshold*zoom &&
616 y <= r.y() + RectEdgeSelectThreshold*zoom;
617 }
618
rect_bottom_edge_selected(const rect & r,int x,int y,int zoom)619 bool rect_bottom_edge_selected(const rect& r, int x, int y, int zoom) {
620 return x >= r.x() - RectEdgeSelectThreshold*zoom &&
621 x <= r.x2() + RectEdgeSelectThreshold*zoom &&
622 y >= r.y2() - RectEdgeSelectThreshold*zoom &&
623 y <= r.y2() + RectEdgeSelectThreshold*zoom;
624 }
625
626 std::vector<editor::tileset> tilesets;
627
628 std::vector<editor::enemy_type> enemy_types;
629
630 int selected_property = 0;
631
632 }
633
~manager()634 editor::manager::~manager() {
635 enemy_types.clear();
636 }
637
enemy_type(const std::string & type,const std::string & category,variant frame_info)638 editor::enemy_type::enemy_type(const std::string& type, const std::string& category, variant frame_info)
639 : category(category), frame_info_(frame_info)
640 {
641 variant_builder new_node;
642 new_node.add("type", type);
643 new_node.add("custom", true);
644 new_node.add("face_right", false);
645 new_node.add("x", 1500);
646 new_node.add("y", 0);
647
648 node = new_node.build();
649 }
650
preview_object() const651 const entity_ptr& editor::enemy_type::preview_object() const
652 {
653 if(!preview_object_) {
654 preview_object_ = entity::build(node);
655 }
656
657 return preview_object_;
658 }
659
preview_frame() const660 const boost::shared_ptr<const frame>& editor::enemy_type::preview_frame() const
661 {
662 if(!preview_frame_) {
663 if(frame_info_.is_map() && !preview_object_) {
664 preview_frame_.reset(new frame(frame_info_));
665 } else {
666 std::cerr << "COULD NOT READ FROM FRAME: " << frame_info_.write_json() << "\n";
667 preview_frame_.reset(new frame(preview_object()->current_frame()));
668 }
669 }
670
671 return preview_frame_;
672 }
673
init(variant node)674 void editor::tileset::init(variant node)
675 {
676 foreach(variant tileset_node, node["tileset"].as_list()) {
677 tilesets.push_back(editor::tileset(tileset_node));
678 }
679 }
680
tileset(variant node)681 editor::tileset::tileset(variant node)
682 : category(node["category"].as_string()), type(node["type"].as_string()),
683 zorder(node["zorder"].as_int()),
684 x_speed(node["x_speed"].as_int(100)),
685 y_speed(node["y_speed"].as_int(100)),
686 sloped(node["sloped"].as_bool()),
687 node_info(node)
688 {
689 }
690
preview() const691 boost::shared_ptr<tile_map> editor::tileset::preview() const
692 {
693 if(!preview_ && node_info.has_key("preview")) {
694 preview_.reset(new tile_map(node_info["preview"]));
695 }
696
697 return preview_;
698 }
699
700
get_editor(const char * level_cfg)701 editor* editor::get_editor(const char* level_cfg)
702 {
703 editor*& e = all_editors[level_cfg];
704 if(!e) {
705 e = new editor(level_cfg);
706 }
707 e->done_ = false;
708 return e;
709 }
710
edit(const char * level_cfg,int xpos,int ypos)711 void editor::edit(const char* level_cfg, int xpos, int ypos)
712 {
713 editor* e = get_editor(level_cfg);
714
715 if(xpos != -1) {
716 e->xpos_ = xpos;
717 e->ypos_ = ypos;
718 }
719
720 editor_resolution_manager resolution_manager(e->xres(), e->yres());
721
722 e->setup_for_editing();
723 e->edit_level();
724 if(g_last_edited_level() != level_cfg) {
725 //a new level was set, so start editing it now.
726 edit(g_last_edited_level().c_str());
727 }
728
729 clear_level_wml();
730 }
731
last_edited_level()732 std::string editor::last_edited_level()
733 {
734 return g_last_edited_level();
735 }
736
737 namespace {
738 int g_codebar_width = 0;
739 }
740
sidebar_width()741 int editor::sidebar_width()
742 {
743 return g_codebar_width == 0 ? 180 : g_codebar_width;
744 }
745
codebar_height()746 int editor::codebar_height()
747 {
748 return 0; //g_codebar_height;
749 }
750
editor(const char * level_cfg)751 editor::editor(const char* level_cfg)
752 : zoom_(1), xpos_(0), ypos_(0), anchorx_(0), anchory_(0),
753 selected_entity_startx_(0), selected_entity_starty_(0),
754 filename_(level_cfg), tool_(TOOL_ADD_RECT),
755 done_(false), face_right_(true), upside_down_(false),
756 cur_tileset_(0), cur_object_(0), cur_hex_tileset_(0),
757 current_dialog_(NULL),
758 drawing_rect_(false), dragging_(false), level_changed_(0),
759 selected_segment_(-1), prev_mousex_(-1), prev_mousey_(-1),
760 xres_(0), yres_(0)
761 {
762 fprintf(stderr, "BEGIN EDITOR::EDITOR\n");
763 const int begin = SDL_GetTicks();
764 preferences::set_record_history(true);
765
766 static bool first_time = true;
767 if(first_time) {
768 variant editor_cfg = json::parse_from_file("data/editor.cfg");
769 const int begin = SDL_GetTicks();
770 tile_map::load_all();
771 const int mid = SDL_GetTicks();
772 fprintf(stderr, "tile_map::load_all(): %dms\n", mid - begin);
773 tileset::init(editor_cfg);
774 fprintf(stderr, "tileset::init(): %dms\n", SDL_GetTicks() - mid);
775 first_time = false;
776 if(editor_cfg.is_map()) {
777 if(editor_cfg["resolution"].is_null() == false) {
778 std::vector<int> v = editor_cfg["resolution"].as_list_int();
779 xres_ = v[0];
780 yres_ = v[1];
781 }
782 }
783 }
784
785 assert(!tilesets.empty());
786 lvl_.reset(new level(level_cfg));
787 lvl_->set_editor();
788 lvl_->finish_loading();
789 lvl_->set_as_current_level();
790
791 levels_.push_back(lvl_);
792
793 editor_menu_dialog_.reset(new editor_menu_dialog(*this));
794 editor_mode_dialog_.reset(new editor_mode_dialog(*this));
795
796 property_dialog_.reset(new editor_dialogs::property_editor_dialog(*this));
797
798 if(preferences::external_code_editor().is_null() == false && !external_code_editor_) {
799 external_code_editor_ = external_text_editor::create(preferences::external_code_editor());
800 }
801 fprintf(stderr, "END EDITOR::EDITOR: %dms\n", SDL_GetTicks() - begin);
802 }
803
~editor()804 editor::~editor()
805 {
806 }
807
group_selection()808 void editor::group_selection()
809 {
810 std::vector<boost::function<void()> > undo, redo;
811
812 foreach(level_ptr lvl, levels_) {
813 const int group = lvl->add_group();
814 foreach(const entity_ptr& e, lvl_->editor_selection()) {
815 entity_ptr c = lvl->get_entity_by_label(e->label());
816 if(!c) {
817 continue;
818 }
819
820 undo.push_back(boost::bind(&level::set_character_group, lvl.get(), c, c->group()));
821 redo.push_back(boost::bind(&level::set_character_group, lvl.get(), c, group));
822 }
823 }
824
825 execute_command(
826 boost::bind(execute_functions, redo),
827 boost::bind(execute_functions, undo));
828 }
829
toggle_facing()830 void editor::toggle_facing()
831 {
832 face_right_ = !face_right_;
833 if(character_dialog_) {
834 character_dialog_->init();
835 }
836 }
837
toggle_upside_down()838 void editor::toggle_upside_down()
839 {
840 upside_down_ = !upside_down_;
841 if(character_dialog_) {
842 character_dialog_->init();
843 }
844 }
845
duplicate_selected_objects()846 void editor::duplicate_selected_objects()
847 {
848 std::vector<boost::function<void()> > redo, undo;
849 foreach(const entity_ptr& c, lvl_->editor_selection()) {
850 entity_ptr duplicate_obj = c->clone();
851
852 foreach(level_ptr lvl, levels_) {
853 entity_ptr obj = duplicate_obj->backup();
854 if(!place_entity_in_level_with_large_displacement(*lvl, *obj)) {
855 continue;
856 }
857
858 redo.push_back(boost::bind(&editor::add_object_to_level, this, lvl, duplicate_obj));
859 undo.push_back(boost::bind(&editor::remove_object_from_level, this, lvl, duplicate_obj));
860 }
861 }
862
863 execute_command(
864 boost::bind(execute_functions, redo),
865 boost::bind(execute_functions, undo));
866 }
867
process_ghost_objects()868 void editor::process_ghost_objects()
869 {
870 if(editing_level_being_played()) {
871 return;
872 }
873
874 lvl_->swap_chars(ghost_objects_);
875
876 const size_t num_chars_before = lvl_->get_chars().size();
877 const std::vector<entity_ptr> chars = lvl_->get_chars();
878 foreach(const entity_ptr& p, chars) {
879 p->process(*lvl_);
880 }
881
882 foreach(const entity_ptr& p, chars) {
883 p->handle_event(OBJECT_EVENT_DRAW);
884 }
885
886 lvl_->swap_chars(ghost_objects_);
887
888 foreach(entity_ptr& p, ghost_objects_) {
889 if(p && p->destroyed()) {
890 lvl_->remove_character(p);
891 p = entity_ptr();
892 }
893 }
894
895 ghost_objects_.erase(std::remove(ghost_objects_.begin(), ghost_objects_.end(), entity_ptr()), ghost_objects_.end());
896 }
897
remove_ghost_objects()898 void editor::remove_ghost_objects()
899 {
900 foreach(entity_ptr c, ghost_objects_) {
901 lvl_->remove_character(c);
902 }
903 }
904
905 namespace {
get_mouse_state(int & mousex,int & mousey)906 unsigned int get_mouse_state(int& mousex, int& mousey) {
907 unsigned int res = SDL_GetMouseState(&mousex, &mousey);
908 mousex = (mousex*graphics::screen_width())/preferences::virtual_screen_width();
909 mousey = (mousey*graphics::screen_height())/preferences::virtual_screen_height();
910
911 return res;
912 }
913
914 int editor_resolution_manager_count = 0;
915
916 int editor_x_resolution = 0, editor_y_resolution = 0;
917
918 }
919
is_active()920 bool editor_resolution_manager::is_active()
921 {
922 return editor_resolution_manager_count != 0;
923 }
924
editor_resolution_manager(int xres,int yres)925 editor_resolution_manager::editor_resolution_manager(int xres, int yres) :
926 original_width_(preferences::actual_screen_width()),
927 original_height_(preferences::actual_screen_height()) {
928 std::cerr << "EDITOR RESOLUTION MANAGER: " << xres << ", " << yres << "\n";
929 if(!editor_x_resolution) {
930 if(xres != 0 && yres != 0) {
931 editor_x_resolution = xres;
932 editor_y_resolution = yres;
933 } else {
934 editor_x_resolution = 1200; //preferences::actual_screen_width() + EDITOR_SIDEBAR_WIDTH + editor_dialogs::LAYERS_DIALOG_WIDTH;
935 editor_y_resolution = preferences::actual_screen_height() + EDITOR_MENUBAR_HEIGHT;
936 }
937 }
938
939 if(++editor_resolution_manager_count == 1) {
940 std::cerr << "EDITOR RESOLUTION: " << editor_x_resolution << "," << editor_y_resolution << "\n";
941 SDL_Surface* result = graphics::set_video_mode(editor_x_resolution,editor_y_resolution,0,SDL_OPENGL|SDL_RESIZABLE|(preferences::fullscreen() ? SDL_FULLSCREEN : 0));
942
943 if(result) {
944 preferences::set_actual_screen_width(editor_x_resolution);
945 preferences::set_actual_screen_height(editor_y_resolution);
946 } else {
947 editor_x_resolution = preferences::actual_screen_width();
948 editor_y_resolution = preferences::actual_screen_height();
949 }
950 }
951 }
952
~editor_resolution_manager()953 editor_resolution_manager::~editor_resolution_manager() {
954 if(--editor_resolution_manager_count == 0) {
955 preferences::set_actual_screen_width(original_width_);
956 preferences::set_actual_screen_height(original_height_);
957 graphics::set_video_mode(original_width_,original_height_,0,SDL_OPENGL|(preferences::resizable() ? SDL_RESIZABLE : 0)|(preferences::fullscreen() ? SDL_FULLSCREEN : 0));
958 }
959 }
960
setup_for_editing()961 void editor::setup_for_editing()
962 {
963 stats::flush();
964 try {
965 load_stats();
966 } catch(...) {
967 debug_console::add_message("Error parsing stats");
968 std::cerr << "ERROR LOADING STATS\n";
969 }
970
971 lvl_->set_as_current_level();
972
973 foreach(level_ptr lvl, levels_) {
974 foreach(entity_ptr c, lvl->get_chars()) {
975 if(entity_collides_with_level(*lvl, *c, MOVE_NONE)) {
976 const int x = c->x();
977 const int y = c->y();
978 if(place_entity_in_level_with_large_displacement(*lvl, *c)) {
979 assert(c->allow_level_collisions() || !entity_collides_with_level(*lvl, *c, MOVE_NONE));
980 if(lvl == lvl_) {
981 debug_console::add_message(formatter() << "Adjusted position of " << c->debug_description() << " to fit: (" << x << "," << y << ") -> (" << c->x() << "," << c->y() << ")");
982 }
983 } else {
984 debug_console::add_message(formatter() << c->debug_description() << " is in an illegal position and can't be auto-corrected");
985 }
986 }
987 }
988 }
989
990 g_last_edited_level() = filename_;
991
992 tileset_dialog_.reset(new editor_dialogs::tileset_editor_dialog(*this));
993 layers_dialog_.reset(new editor_dialogs::editor_layers_dialog(*this));
994 current_dialog_ = tileset_dialog_.get();
995
996 //reset the tool status.
997 change_tool(tool_);
998 }
999
edit_level()1000 void editor::edit_level()
1001 {
1002
1003 glEnable(GL_BLEND);
1004 #if !defined(USE_GLES2)
1005 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
1006 glEnable(GL_TEXTURE_2D);
1007 #endif
1008 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1009
1010 int selected_tile = 0;
1011
1012 done_ = false;
1013 prev_mousex_ = prev_mousey_ = -1;
1014 while(!done_) {
1015 const int scheduled_frame_end_time = SDL_GetTicks() + 20;
1016
1017 handle_scrolling();
1018 process();
1019
1020 SDL_Event event;
1021 while(SDL_PollEvent(&event) && !done_) {
1022 handle_event(event, false);
1023 }
1024
1025 draw();
1026
1027 SDL_Delay(std::max<int>(1, scheduled_frame_end_time - SDL_GetTicks()));
1028 }
1029 }
1030
handle_event(const SDL_Event & event,bool swallowed)1031 bool editor::handle_event(const SDL_Event& event, bool swallowed)
1032 {
1033 const bool dialog_started_with_focus = code_dialog_ && code_dialog_->has_focus() || current_dialog_ && current_dialog_->has_focus();
1034 if(code_dialog_ && code_dialog_->process_event(event, swallowed)) {
1035 return true;
1036 }
1037
1038 if(swallowed) {
1039 return true;
1040 }
1041
1042 // if(done_) {
1043 // return false;
1044 // }
1045
1046 if(editor_menu_dialog_->process_event(event, false)) {
1047 return true;
1048 }
1049
1050 if(editor_mode_dialog_->process_event(event, false)) {
1051 return true;
1052 }
1053
1054 if(current_dialog_ && current_dialog_->process_event(event, false)) {
1055 return true;
1056 }
1057
1058 if(layers_dialog_ && layers_dialog_->process_event(event, false)) {
1059 return true;
1060 }
1061
1062 switch(event.type) {
1063 case SDL_QUIT:
1064 done_ = true;
1065 break;
1066 case SDL_KEYDOWN:
1067 if(event.key.keysym.sym == SDLK_ESCAPE) {
1068 if(confirm_quit()) {
1069 done_ = true;
1070 return true;
1071 }
1072 }
1073
1074 handle_key_press(event.key);
1075 break;
1076 case SDL_MOUSEBUTTONDOWN:
1077 if(!dialog_started_with_focus) {
1078 //if the code dialog started with focus, we ignore mouse
1079 //presses so that the first click just unfocuses it.
1080 handle_mouse_button_down(event.button);
1081 }
1082 break;
1083
1084 case SDL_MOUSEBUTTONUP:
1085 if(!dialog_started_with_focus) {
1086 //if the code dialog started with focus, we ignore mouse
1087 //presses so that the first click just unfocuses it.
1088 handle_mouse_button_up(event.button);
1089 }
1090 break;
1091 case SDL_VIDEORESIZE: {
1092 const SDL_ResizeEvent* const resize = reinterpret_cast<const SDL_ResizeEvent*>(&event);
1093
1094 SDL_Surface* result = graphics::set_video_mode(resize->w,resize->h,0,SDL_OPENGL|SDL_RESIZABLE|(preferences::fullscreen() ? SDL_FULLSCREEN : 0));
1095
1096 if(result) {
1097 editor_x_resolution = resize->w;
1098 editor_y_resolution = resize->h;
1099 preferences::set_actual_screen_width(resize->w);
1100 preferences::set_actual_screen_height(resize->h);
1101
1102 reset_dialog_positions();
1103 }
1104
1105 return false;
1106 }
1107
1108 default:
1109 break;
1110 }
1111
1112 return false;
1113 }
1114
process()1115 void editor::process()
1116 {
1117 if(code_dialog_) {
1118 code_dialog_->process();
1119 }
1120
1121 if(external_code_editor_) {
1122 external_code_editor_->process();
1123 }
1124
1125 if(layers_dialog_) {
1126 layers_dialog_->process();
1127 }
1128
1129 if(external_code_editor_ && external_code_editor_->replace_in_game_editor() && editor_menu_dialog_) {
1130 std::string type;
1131 if(lvl_->editor_selection().empty() == false) {
1132 type = lvl_->editor_selection().back()->query_value("type").as_string();
1133 }
1134 if(type.empty() == false) {
1135 editor_menu_dialog_->set_code_button_text("edit " + type);
1136 } else {
1137 editor_menu_dialog_->set_code_button_text("");
1138 }
1139 }
1140
1141 if(editor_mode_dialog_) {
1142 editor_mode_dialog_->refresh_selection();
1143 }
1144
1145 g_codebar_width = code_dialog_ ? code_dialog_->width() : 0;
1146
1147 if(code_dialog_ && code_dialog_->has_keyboard_focus()) {
1148 return;
1149 }
1150
1151 process_ghost_objects();
1152
1153 int mousex, mousey;
1154 const unsigned int buttons = get_mouse_state(mousex, mousey);
1155 #if defined(__ANDROID__) && SDL_VERSION_ATLEAST(1, 3, 0)
1156 const Uint8* keystate = SDL_GetKeyboardState(0);
1157 #else
1158 const Uint8* keystate = SDL_GetKeyState(NULL);
1159 #endif
1160
1161 if(buttons == 0) {
1162 drawing_rect_ = false;
1163 }
1164
1165 //make middle-clicking drag the screen around.
1166 if(prev_mousex_ != -1 && prev_mousey_ != -1 && (buttons&SDL_BUTTON_MIDDLE)) {
1167 const int diff_x = mousex - prev_mousex_;
1168 const int diff_y = mousey - prev_mousey_;
1169 xpos_ -= diff_x*zoom_;
1170 ypos_ -= diff_y*zoom_;
1171 }
1172
1173 prev_mousex_ = mousex;
1174 prev_mousey_ = mousey;
1175
1176
1177 const int selectx = round_tile_size(xpos_ + mousex*zoom_);
1178 const int selecty = round_tile_size(ypos_ + mousey*zoom_);
1179
1180 const bool object_mode = (tool() == TOOL_ADD_OBJECT || tool() == TOOL_SELECT_OBJECT);
1181 if(property_dialog_ && g_variable_editing) {
1182 const int diffx = (xpos_ + mousex*zoom_) - anchorx_;
1183 const int diffy = (ypos_ + mousey*zoom_) - anchory_;
1184 int diff = 0;
1185 switch(g_variable_editing->type()) {
1186 case editor_variable_info::XPOSITION:
1187 diff = diffx;
1188 break;
1189 case editor_variable_info::YPOSITION:
1190 diff = diffy;
1191 break;
1192 default:
1193 break;
1194 }
1195
1196 if(property_dialog_ && property_dialog_->get_entity()) {
1197 variant new_value;
1198 const bool ctrl_pressed = (SDL_GetModState()&(KMOD_LCTRL|KMOD_RCTRL)) != 0;
1199
1200 if(g_variable_editing->type() == editor_variable_info::TYPE_POINTS) {
1201 std::vector<variant> items = g_variable_editing_original_value.as_list();
1202 ASSERT_LOG(g_variable_editing_index >= 0 && g_variable_editing_index < items.size(), "Variable editing points invalid: " << g_variable_editing_index << " / " << items.size());
1203 point orig_point(items[g_variable_editing_index]);
1204 point new_point(orig_point.x + diffx, orig_point.y + diffy);
1205 if(!ctrl_pressed) {
1206 new_point.x = new_point.x - new_point.x%(TileSize/2);
1207 new_point.y = new_point.y - new_point.y%(TileSize/2);
1208 }
1209 items[g_variable_editing_index] = new_point.write();
1210 new_value = variant(&items);
1211
1212 } else {
1213 int new_value_int = g_variable_editing_original_value.as_int() + diff;
1214 if(!ctrl_pressed) {
1215 new_value_int = new_value_int - new_value_int%(TileSize/2);
1216 }
1217
1218 new_value = variant(new_value_int);
1219 }
1220
1221 if(!new_value.is_null()) {
1222 std::vector<boost::function<void()> > undo, redo;
1223 generate_mutate_commands(property_dialog_->get_entity(), g_variable_editing->variable_name(), new_value, undo, redo);
1224 execute_command(
1225 boost::bind(execute_functions, redo),
1226 boost::bind(execute_functions, undo));
1227
1228 //We don't want this to actually be undoable, since the whole
1229 //drag operation will be undoable when we're done, so remove
1230 //from the undo stack.
1231 undo_.pop_back();
1232 }
1233 }
1234 } else if(object_mode && !buttons) {
1235 //remove ghost objects and re-add them. This guarantees ghost
1236 //objects always remain at the end of the level ordering.
1237 remove_ghost_objects();
1238 entity_ptr c = lvl_->get_next_character_at_point(xpos_ + mousex*zoom_, ypos_ + mousey*zoom_, xpos_, ypos_);
1239 foreach(const entity_ptr& ghost, ghost_objects_) {
1240 lvl_->add_character(ghost);
1241 }
1242
1243 lvl_->set_editor_highlight(c);
1244 //See if we should add ghost objects. Human objects don't get
1245 //ghost (it doesn't make much sense for them to do so)
1246 if(ghost_objects_.empty() && c && !c->is_human() && !editing_level_being_played()) {
1247 //we have an object but no ghost for it, make the
1248 //object's ghost and deploy it.
1249 entity_ptr clone = c->clone();
1250 if(clone && !entity_collides_with_level(*lvl_, *clone, MOVE_NONE)) {
1251 ghost_objects_.push_back(clone);
1252 lvl_->add_character(clone);
1253
1254 //fire the event to tell the ghost it's been added.
1255 lvl_->swap_chars(ghost_objects_);
1256 clone->handle_event(OBJECT_EVENT_START_LEVEL);
1257 lvl_->swap_chars(ghost_objects_);
1258 }
1259 } else if(ghost_objects_.empty() == false && !c) {
1260 //ghost objects are present but we are no longer moused-over
1261 //an object, so remove the ghosts.
1262 remove_ghost_objects();
1263 ghost_objects_.clear();
1264 }
1265 } else if(object_mode && lvl_->editor_highlight()) {
1266 foreach(level_ptr lvl, levels_) {
1267 lvl->set_editor_dragging_objects();
1268 }
1269
1270 //we're handling objects, and a button is down, and we have an
1271 //object under the mouse. This means we are dragging something.
1272
1273 // check if cursor is not in the sidebar!
1274 if (mousex < editor_mode_dialog_->x()) {
1275 handle_object_dragging(mousex, mousey);
1276 }
1277 } else if(drawing_rect_) {
1278 handle_drawing_rect(mousex, mousey);
1279 }
1280
1281 if(!object_mode) {
1282 //not in object mode, the picker still highlights objects,
1283 //though it won't create ghosts, so remove all ghosts.
1284 if(tool() == TOOL_PICKER) {
1285 entity_ptr c = lvl_->get_next_character_at_point(xpos_ + mousex*zoom_, ypos_ + mousey*zoom_, xpos_, ypos_);
1286 lvl_->set_editor_highlight(c);
1287 } else {
1288 lvl_->set_editor_highlight(entity_ptr());
1289 }
1290
1291 remove_ghost_objects();
1292 ghost_objects_.clear();
1293 }
1294
1295 //if we're drawing with a pencil see if we add a new tile
1296 if(tool() == TOOL_PENCIL && dragging_ && buttons) {
1297 const int xpos = xpos_ + mousex*zoom_;
1298 const int ypos = ypos_ + mousey*zoom_;
1299 point p(xpos, ypos);
1300 if(std::find(g_current_draw_tiles.begin(), g_current_draw_tiles.end(), p) == g_current_draw_tiles.end()) {
1301 g_current_draw_tiles.push_back(p);
1302
1303 if(buttons&SDL_BUTTON_LEFT) {
1304 add_tile_rect(p.x, p.y, p.x, p.y);
1305 } else {
1306 remove_tile_rect(p.x, p.y, p.x, p.y);
1307 }
1308 }
1309 }
1310
1311 if(tool() == TOOL_EDIT_HEXES && dragging_ && buttons) {
1312 const int xpos = xpos_ + mousex*zoom_;
1313 const int ypos = ypos_ + mousey*zoom_;
1314 point p(xpos, ypos);
1315 if(std::find(g_current_draw_hex_tiles.begin(), g_current_draw_hex_tiles.end(), p) == g_current_draw_hex_tiles.end()) {
1316 g_current_draw_hex_tiles.push_back(p);
1317
1318 if(buttons&SDL_BUTTON_LEFT) {
1319 add_hex_tile_rect(p.x, p.y, p.x, p.y);
1320 } else {
1321 remove_hex_tile_rect(p.x, p.y, p.x, p.y);
1322 }
1323 }
1324 }
1325
1326 foreach(level_ptr lvl, levels_) {
1327 lvl->complete_rebuild_tiles_in_background();
1328 }
1329 }
1330
set_pos(int x,int y)1331 void editor::set_pos(int x, int y)
1332 {
1333 xpos_ = x;
1334 ypos_ = y;
1335 }
1336
set_playing_level(level_ptr lvl)1337 void editor::set_playing_level(level_ptr lvl)
1338 {
1339 levels_.resize(1);
1340 levels_.push_back(lvl);
1341 lvl_ = lvl;
1342 }
1343
toggle_active_level()1344 void editor::toggle_active_level()
1345 {
1346 std::vector<level_ptr>::iterator i = std::find(levels_.begin(), levels_.end(), lvl_);
1347 if(i != levels_.end()) {
1348 ++i;
1349 if(i == levels_.end()) {
1350 i = levels_.begin();
1351 }
1352
1353 lvl_ = *i;
1354 }
1355 lvl_->set_as_current_level();
1356 }
1357
editing_level_being_played() const1358 bool editor::editing_level_being_played() const
1359 {
1360 return levels_.size() == 2 && std::find(levels_.begin(), levels_.end(), lvl_) != levels_.begin();
1361 }
1362
reset_dialog_positions()1363 void editor::reset_dialog_positions()
1364 {
1365 if(editor_mode_dialog_) {
1366 editor_mode_dialog_->set_loc(graphics::screen_width() - editor_mode_dialog_->width(), editor_mode_dialog_->y());
1367 }
1368
1369 #define SET_DIALOG_POS(d) if(d) { \
1370 d->set_loc(graphics::screen_width() - d->width(), d->y()); \
1371 \
1372 d->set_dim(d->width(), \
1373 std::max<int>(10, preferences::actual_screen_height() - d->y())); \
1374 }
1375 SET_DIALOG_POS(character_dialog_);
1376 SET_DIALOG_POS(property_dialog_);
1377 SET_DIALOG_POS(tileset_dialog_);
1378 #undef SET_DIALOG_POS
1379
1380 if(layers_dialog_ && editor_mode_dialog_) {
1381 layers_dialog_->set_loc(editor_mode_dialog_->x() - layers_dialog_->width(), EDITOR_MENUBAR_HEIGHT);
1382 layers_dialog_->set_dim(layers_dialog_->width(), preferences::actual_screen_height() - EDITOR_MENUBAR_HEIGHT);
1383 }
1384
1385 if(editor_menu_dialog_ && editor_mode_dialog_) {
1386 editor_menu_dialog_->set_dim(preferences::actual_screen_width() - editor_mode_dialog_->width(), editor_menu_dialog_->height());
1387 }
1388 }
1389
1390 namespace {
sort_entity_zsub_orders(const entity_ptr & a,const entity_ptr & b)1391 bool sort_entity_zsub_orders(const entity_ptr& a, const entity_ptr& b) {
1392 return a->zsub_order() < b->zsub_order();
1393 }
1394 }
1395
execute_shift_object(entity_ptr e,int dx,int dy)1396 void editor::execute_shift_object(entity_ptr e, int dx, int dy)
1397 {
1398 begin_command_group();
1399 foreach(level_ptr lvl, levels_) {
1400 entity_ptr obj = lvl->get_entity_by_label(e->label());
1401 if(obj) {
1402 execute_command(boost::bind(&editor::move_object, this, lvl, obj, obj->x()+dx,obj->y()+dy),
1403 boost::bind(&editor::move_object,this, lvl, obj,obj->x(),obj->y()));
1404 }
1405 }
1406 end_command_group();
1407 }
1408
handle_key_press(const SDL_KeyboardEvent & key)1409 void editor::handle_key_press(const SDL_KeyboardEvent& key)
1410 {
1411 if(key.keysym.sym == SDLK_e && (key.keysym.mod&KMOD_ALT) && levels_.size() > 1) {
1412 done_ = true;
1413 return;
1414 }
1415
1416 if(key.keysym.sym == SDLK_s && (key.keysym.mod&KMOD_ALT)) {
1417 IMG_SaveFrameBuffer((std::string(preferences::user_data_path()) + "screenshot.png").c_str(), 5);
1418 }
1419
1420 if(key.keysym.sym == SDLK_1 && key.keysym.mod&KMOD_CTRL) {
1421 duplicate_selected_objects();
1422 }
1423
1424 if(key.keysym.sym == SDLK_u) {
1425 undo_command();
1426 }
1427
1428 if(key.keysym.sym == SDLK_r &&
1429 !(key.keysym.mod&KMOD_CTRL)) {
1430 redo_command();
1431 }
1432
1433 if(key.keysym.sym == SDLK_z) {
1434 zoom_in();
1435 }
1436
1437 if(key.keysym.sym == SDLK_h) {
1438 preferences::toogle_debug_hitboxes();
1439 }
1440
1441 if(key.keysym.sym == SDLK_KP8) {
1442 begin_command_group();
1443 foreach(const entity_ptr& e, lvl_->editor_selection()){
1444 execute_shift_object(e, 0, -2);
1445 }
1446 end_command_group();
1447 }
1448
1449 if(key.keysym.sym == SDLK_KP5) {
1450 begin_command_group();
1451 foreach(const entity_ptr& e, lvl_->editor_selection()){
1452 execute_shift_object(e, 0, 2);
1453 }
1454 end_command_group();
1455 }
1456
1457 if(key.keysym.sym == SDLK_KP4) {
1458 begin_command_group();
1459 foreach(const entity_ptr& e, lvl_->editor_selection()){
1460 execute_shift_object(e, -2, 0);
1461 }
1462 end_command_group();
1463 }
1464
1465 if(key.keysym.sym == SDLK_KP6) {
1466 begin_command_group();
1467 foreach(const entity_ptr& e, lvl_->editor_selection()){
1468 execute_shift_object(e, 2, 0);
1469 }
1470 end_command_group();
1471 }
1472
1473 if(key.keysym.sym == SDLK_EQUALS || key.keysym.sym == SDLK_MINUS ) {
1474 if(lvl_->editor_selection().size() > 1){
1475
1476 //store them in a new container
1477 std::vector <entity_ptr> v2;
1478 foreach(const entity_ptr& e, lvl_->editor_selection()){
1479 v2.push_back(e.get());
1480 }
1481 //sort this container in ascending zsub_order
1482 std::sort(v2.begin(),v2.end(), sort_entity_zsub_orders);
1483
1484 //if it was +, then move the backmost object in front of the frontmost object.
1485 //if it was -, do vice versa (frontmost object goes behind backmost object)
1486 if(key.keysym.sym == SDLK_EQUALS){
1487 begin_command_group();
1488 foreach(level_ptr lvl, levels_) {
1489 entity_ptr obj = lvl->get_entity_by_label(v2.front()->label());
1490 if(obj) {
1491 execute_command(boost::bind(&entity::set_zsub_order, obj, v2.back()->zsub_order()+1),
1492 boost::bind(&entity::set_zsub_order, obj, v2.front()->zsub_order() ));
1493 }
1494 }
1495 end_command_group();
1496 }else if(key.keysym.sym == SDLK_MINUS){
1497 begin_command_group();
1498 foreach(level_ptr lvl, levels_) {
1499 entity_ptr obj = lvl->get_entity_by_label(v2.back()->label());
1500 if(obj) {
1501 execute_command(boost::bind(&entity::set_zsub_order, obj, v2.front()->zsub_order()-1),
1502 boost::bind(&entity::set_zsub_order, obj, v2.back()->zsub_order() ));
1503
1504 }
1505 }
1506 end_command_group();
1507 }
1508 }
1509 }
1510
1511
1512 if(key.keysym.sym == SDLK_x) {
1513 zoom_out();
1514 }
1515
1516 if(key.keysym.sym == SDLK_f) {
1517 lvl_->set_show_foreground(!lvl_->show_foreground());
1518 }
1519
1520 if(key.keysym.sym == SDLK_b) {
1521 lvl_->set_show_background(!lvl_->show_background());
1522 }
1523
1524 if(editing_objects() && (key.keysym.sym == SDLK_DELETE || key.keysym.sym == SDLK_BACKSPACE) && lvl_->editor_selection().empty() == false) {
1525 //deleting objects. We clear the selection as well as
1526 //deleting. To undo, the previous selection will be cleared,
1527 //and then the deleted objects re-selected.
1528 std::vector<boost::function<void()> > redo, undo;
1529 undo.push_back(boost::bind(&level::editor_clear_selection, lvl_.get()));
1530
1531 //if we undo, return the objects to the property dialog
1532 undo.push_back(boost::bind(&editor_dialogs::property_editor_dialog::set_entity_group, property_dialog_.get(), lvl_->editor_selection()));
1533 redo.push_back(boost::bind(&level::editor_clear_selection, lvl_.get()));
1534 //we want to clear the objects in the property dialog
1535 redo.push_back(boost::bind(&editor_dialogs::property_editor_dialog::set_entity_group, property_dialog_.get(), std::vector<entity_ptr>()));
1536 foreach(const entity_ptr& e, lvl_->editor_selection()) {
1537 generate_remove_commands(e, undo, redo);
1538 undo.push_back(boost::bind(&level::editor_select_object, lvl_.get(), e));
1539 }
1540 execute_command(
1541 boost::bind(execute_functions, redo),
1542 boost::bind(execute_functions, undo));
1543 }
1544
1545 if(!tile_selection_.empty() && (key.keysym.sym == SDLK_DELETE || key.keysym.sym == SDLK_BACKSPACE)) {
1546 int min_x = INT_MAX, min_y = INT_MAX, max_x = INT_MIN, max_y = INT_MIN;
1547 std::vector<boost::function<void()> > redo, undo;
1548
1549 foreach(level_ptr lvl, levels_) {
1550 foreach(const point& p, tile_selection_.tiles) {
1551 const int x = p.x*TileSize;
1552 const int y = p.y*TileSize;
1553
1554 min_x = std::min(x, min_x);
1555 max_x = std::max(x, max_x);
1556 min_y = std::min(y, min_y);
1557 max_y = std::max(y, max_y);
1558
1559 redo.push_back(boost::bind(&level::clear_tile_rect, lvl.get(), x, y, x, y));
1560 std::map<int, std::vector<std::string> > old_tiles;
1561 lvl->get_all_tiles_rect(x, y, x, y, old_tiles);
1562 for(std::map<int, std::vector<std::string> >::const_iterator i = old_tiles.begin(); i != old_tiles.end(); ++i) {
1563 undo.push_back(boost::bind(&level::add_tile_rect_vector, lvl.get(), i->first, x, y, x, y, i->second));
1564 }
1565 }
1566
1567 if(!tile_selection_.tiles.empty()) {
1568 undo.push_back(boost::bind(&level::start_rebuild_tiles_in_background, lvl.get(), std::vector<int>()));
1569 redo.push_back(boost::bind(&level::start_rebuild_tiles_in_background, lvl.get(), std::vector<int>()));
1570 }
1571 }
1572
1573 execute_command(
1574 boost::bind(execute_functions, redo),
1575 boost::bind(execute_functions, undo));
1576 }
1577
1578 if(key.keysym.sym == SDLK_o) {
1579 editor_menu_dialog_->open_level();
1580 }
1581
1582 if(key.keysym.sym == SDLK_s && (key.keysym.mod&KMOD_CTRL)) {
1583 save_level();
1584 }
1585
1586 if(key.keysym.sym == SDLK_f) {
1587 toggle_facing();
1588 }
1589
1590 if(key.keysym.sym == SDLK_i) {
1591 toggle_upside_down();
1592 }
1593
1594 if(key.keysym.sym == SDLK_r &&
1595 (key.keysym.mod&KMOD_CTRL) && levels_.size() == 2 &&
1596 lvl_ == levels_.back()) {
1597
1598 entity_ptr player;
1599 if(lvl_->player()) {
1600 player.reset(&lvl_->player()->get_entity());
1601 }
1602
1603 levels_.front()->transfer_state_to(*levels_.back());
1604
1605 if(player) {
1606 if(place_entity_in_level(*lvl_, *player)) {
1607 lvl_->add_player(player);
1608 }
1609 }
1610
1611 controls::new_level(lvl_->cycle(), lvl_->players().empty() ? 1 : lvl_->players().size(), multiplayer::slot());
1612
1613 }
1614
1615 if(key.keysym.sym == SDLK_c) {
1616 foreach(const entity_ptr& obj, lvl_->get_chars()) {
1617 if(entity_collides_with_level(*lvl_, *obj, MOVE_NONE)) {
1618 xpos_ = obj->x() - graphics::screen_width()/2;
1619 ypos_ = obj->y() - graphics::screen_height()/2;
1620 break;
1621 }
1622 }
1623 }
1624 }
1625
reset_playing_level(bool keep_player)1626 void editor::reset_playing_level(bool keep_player)
1627 {
1628 if(levels_.size() == 2 && lvl_ == levels_.back()) {
1629 entity_ptr player;
1630 if(keep_player && lvl_->player()) {
1631 player.reset(&lvl_->player()->get_entity());
1632 }
1633
1634 levels_.front()->transfer_state_to(*levels_.back());
1635
1636 if(player) {
1637 if(place_entity_in_level(*lvl_, *player)) {
1638 lvl_->add_player(player);
1639 }
1640 }
1641
1642 controls::new_level(lvl_->cycle(), lvl_->players().empty() ? 1 : lvl_->players().size(), multiplayer::slot());
1643
1644 }
1645 }
1646
toggle_pause() const1647 void editor::toggle_pause() const
1648 {
1649 if(level_runner::get_current()) {
1650 level_runner::get_current()->toggle_pause();
1651 }
1652 }
1653
handle_scrolling()1654 void editor::handle_scrolling()
1655 {
1656 if(code_dialog_ && code_dialog_->has_keyboard_focus()) {
1657 return;
1658 }
1659
1660 const int ScrollSpeed = 24*zoom_;
1661 const int FastScrollSpeed = 384*zoom_;
1662
1663 if(key_[SDLK_LEFT]) {
1664 xpos_ -= ScrollSpeed;
1665 if(key_[SDLK_KP0]) {
1666 xpos_ -= FastScrollSpeed;
1667 }
1668 }
1669
1670 if(key_[SDLK_RIGHT]) {
1671 xpos_ += ScrollSpeed;
1672 if(key_[SDLK_KP0]) {
1673 xpos_ += FastScrollSpeed;
1674 }
1675 }
1676
1677 if(key_[SDLK_UP]) {
1678 ypos_ -= ScrollSpeed;
1679 if(key_[SDLK_KP0]) {
1680 ypos_ -= FastScrollSpeed;
1681 }
1682 }
1683
1684 if(key_[SDLK_DOWN]) {
1685 ypos_ += ScrollSpeed;
1686 if(key_[SDLK_KP0]) {
1687 ypos_ += FastScrollSpeed;
1688 }
1689 }
1690 }
1691
handle_object_dragging(int mousex,int mousey)1692 void editor::handle_object_dragging(int mousex, int mousey)
1693 {
1694 if(std::count(lvl_->editor_selection().begin(), lvl_->editor_selection().end(), lvl_->editor_highlight()) == 0) {
1695 return;
1696 }
1697
1698 const bool ctrl_pressed = (SDL_GetModState()&(KMOD_LCTRL|KMOD_RCTRL)) != 0;
1699 const int dx = xpos_ + mousex*zoom_ - anchorx_;
1700 const int dy = ypos_ + mousey*zoom_ - anchory_;
1701 const int xpos = selected_entity_startx_ + dx;
1702 const int ypos = selected_entity_starty_ + dy;
1703
1704 const int new_x = xpos - (ctrl_pressed ? 0 : (xpos%TileSize));
1705 const int new_y = ypos - (ctrl_pressed ? 0 : (ypos%TileSize));
1706
1707 const int delta_x = new_x - lvl_->editor_highlight()->x();
1708 const int delta_y = new_y - lvl_->editor_highlight()->y();
1709
1710 //don't move the object from its starting position until the
1711 //delta in movement is large enough.
1712 const bool in_starting_position =
1713 lvl_->editor_highlight()->x() == selected_entity_startx_ &&
1714 lvl_->editor_highlight()->y() == selected_entity_starty_;
1715 const bool too_small_to_move = in_starting_position &&
1716 abs(dx) < 5 && abs(dy) < 5;
1717
1718 if(!too_small_to_move && (new_x != lvl_->editor_highlight()->x() || new_y != lvl_->editor_highlight()->y())) {
1719 std::vector<boost::function<void()> > redo, undo;
1720
1721 foreach(const entity_ptr& e, lvl_->editor_selection()) {
1722 foreach(level_ptr lvl, levels_) {
1723 entity_ptr obj = lvl->get_entity_by_label(e->label());
1724 if(obj) {
1725 redo.push_back(boost::bind(&editor::move_object, this, lvl, obj, e->x() + delta_x, e->y() + delta_y));
1726 undo.push_back(boost::bind(&editor::move_object, this, lvl, obj, obj->x(), obj->y()));
1727 }
1728 }
1729
1730 }
1731
1732 //all dragging that is done should be treated as one operation
1733 //from an undo/redo perspective. So, we see if we're already dragging
1734 //and have performed existing drag operations, and if so we
1735 //roll the previous undo command into this.
1736 boost::function<void()> undo_fn = boost::bind(execute_functions, undo);
1737
1738 if(g_started_dragging_object && undo_.empty() == false && undo_.back().type == COMMAND_TYPE_DRAG_OBJECT) {
1739 undo_fn = undo_.back().undo_command;
1740 undo_command();
1741 }
1742
1743 execute_command(boost::bind(execute_functions, redo), undo_fn, COMMAND_TYPE_DRAG_OBJECT);
1744
1745 g_started_dragging_object = true;
1746
1747 remove_ghost_objects();
1748 ghost_objects_.clear();
1749 }
1750 }
1751
handle_drawing_rect(int mousex,int mousey)1752 void editor::handle_drawing_rect(int mousex, int mousey)
1753 {
1754 const unsigned int buttons = get_mouse_state(mousex, mousey);
1755
1756 const int xpos = xpos_ + mousex*zoom_;
1757 const int ypos = ypos_ + mousey*zoom_;
1758
1759 int x1 = xpos;
1760 int x2 = anchorx_;
1761 int y1 = ypos;
1762 int y2 = anchory_;
1763 if(x1 > x2) {
1764 std::swap(x1, x2);
1765 }
1766
1767 if(y1 > y2) {
1768 std::swap(y1, y2);
1769 }
1770
1771 x1 = round_tile_size(x1);
1772 x2 = round_tile_size(x2 + TileSize);
1773 y1 = round_tile_size(y1);
1774 y2 = round_tile_size(y2 + TileSize);
1775
1776 const rect new_rect = rect(x1, y1, x2 - x1, y2 - y1);
1777 if(g_rect_drawing == new_rect) {
1778 return;
1779 }
1780
1781 if(tool() == TOOL_ADD_RECT) {
1782 lvl_->freeze_rebuild_tiles_in_background();
1783 if(tmp_undo_.get()) {
1784 tmp_undo_->undo_command();
1785 }
1786
1787 if(buttons == SDL_BUTTON_LEFT) {
1788 add_tile_rect(anchorx_, anchory_, xpos, ypos);
1789 } else {
1790 remove_tile_rect(anchorx_, anchory_, xpos, ypos);
1791 }
1792
1793 tmp_undo_.reset(new executable_command(undo_.back()));
1794 undo_.pop_back();
1795 lvl_->unfreeze_rebuild_tiles_in_background();
1796 }
1797 g_rect_drawing = new_rect;
1798 }
1799
handle_mouse_button_down(const SDL_MouseButtonEvent & event)1800 void editor::handle_mouse_button_down(const SDL_MouseButtonEvent& event)
1801 {
1802 if(event.button == SDL_BUTTON_WHEELUP || event.button == SDL_BUTTON_WHEELDOWN) {
1803 return;
1804 }
1805 const bool ctrl_pressed = (SDL_GetModState()&(KMOD_LCTRL|KMOD_RCTRL)) != 0;
1806 const bool shift_pressed = (SDL_GetModState()&(KMOD_LSHIFT|KMOD_RSHIFT)) != 0;
1807 const bool alt_pressed = (SDL_GetModState()&KMOD_ALT) != 0;
1808 int mousex, mousey;
1809 const unsigned int buttons = get_mouse_state(mousex, mousey);
1810
1811 anchorx_ = xpos_ + mousex*zoom_;
1812 anchory_ = ypos_ + mousey*zoom_;
1813 if(event.button == SDL_BUTTON_MIDDLE && !alt_pressed) {
1814 return;
1815 }
1816
1817 resizing_left_level_edge = rect_left_edge_selected(lvl_->boundaries(), anchorx_, anchory_, zoom_);
1818 resizing_right_level_edge = rect_right_edge_selected(lvl_->boundaries(), anchorx_, anchory_, zoom_);
1819 resizing_top_level_edge = rect_top_edge_selected(lvl_->boundaries(), anchorx_, anchory_, zoom_);
1820 resizing_bottom_level_edge = rect_bottom_edge_selected(lvl_->boundaries(), anchorx_, anchory_, zoom_);
1821
1822 if(resizing_left_level_edge || resizing_right_level_edge || resizing_top_level_edge || resizing_bottom_level_edge) {
1823 return;
1824 }
1825
1826 dragging_ = drawing_rect_ = false;
1827
1828 if(adding_points_.empty() == false) {
1829 if(event.button == SDL_BUTTON_LEFT && property_dialog_ && property_dialog_->get_entity()) {
1830 const int xpos = anchorx_;
1831 const int ypos = anchory_;
1832 std::cerr << "ADD POINT: " << xpos << ", " << ypos << "\n";
1833
1834 entity_ptr c = property_dialog_->get_entity();
1835
1836 game_logic::formula_callable* obj_vars = c->query_value("vars").mutable_callable();
1837 variant current_value = obj_vars->query_value(adding_points_);
1838 std::vector<variant> new_value;
1839 if(current_value.is_list()) {
1840 new_value = current_value.as_list();
1841 }
1842
1843 std::vector<variant> point;
1844 point.push_back(variant(xpos));
1845 point.push_back(variant(ypos));
1846 new_value.push_back(variant(&point));
1847
1848 std::vector<boost::function<void()> > redo, undo;
1849 generate_mutate_commands(c, adding_points_, variant(&new_value), undo, redo);
1850
1851 execute_command(
1852 boost::bind(execute_functions, redo),
1853 boost::bind(execute_functions, undo));
1854
1855
1856 start_adding_points(adding_points_);
1857
1858 } else {
1859 start_adding_points("");
1860 }
1861 } else if(tool() == TOOL_EDIT_SEGMENTS) {
1862 if(point_in_rect(point(anchorx_, anchory_), lvl_->boundaries())) {
1863 const int xpos = anchorx_ - lvl_->boundaries().x();
1864 const int ypos = anchory_ - lvl_->boundaries().y();
1865 const int segment = lvl_->segment_width() ? xpos/lvl_->segment_width() : ypos/lvl_->segment_height();
1866
1867 if(selected_segment_ == -1) {
1868 selected_segment_ = segment;
1869 segment_dialog_->set_segment(segment);
1870 } else if(buttons&SDL_BUTTON_RIGHT) {
1871 if(segment != selected_segment_ && selected_segment_ >= 0) {
1872 variant next = lvl_->get_var(formatter() << "segments_after_" << selected_segment_);
1873 std::vector<variant> v;
1874 if(next.is_list()) {
1875 for(int n = 0; n != next.num_elements(); ++n) {
1876 v.push_back(next[n]);
1877 }
1878 }
1879
1880 std::vector<variant>::iterator i = std::find(v.begin(), v.end(), variant(segment));
1881 if(i != v.end()) {
1882 v.erase(i);
1883 } else {
1884 v.push_back(variant(segment));
1885 }
1886
1887 lvl_->set_var(formatter() << "segments_after_" << selected_segment_, variant(&v));
1888 }
1889 }
1890 } else {
1891 selected_segment_ = -1;
1892 segment_dialog_->set_segment(selected_segment_);
1893 }
1894 } else if(tool() == TOOL_PICKER) {
1895 if(lvl_->editor_highlight()) {
1896 change_tool(TOOL_ADD_OBJECT);
1897
1898 variant node = lvl_->editor_highlight()->write();
1899 const std::string type = node["type"].as_string();
1900 for(int n = 0; n != all_characters().size(); ++n) {
1901 const enemy_type& c = all_characters()[n];
1902 if(c.node["type"].as_string() == type) {
1903 character_dialog_->select_category(c.category);
1904 character_dialog_->set_character(n);
1905 return;
1906 }
1907 }
1908 return;
1909 } else {
1910 //pick the top most tile at this point.
1911 std::map<int, std::vector<std::string> > tiles;
1912 lvl_->get_all_tiles_rect(anchorx_, anchory_, anchorx_, anchory_, tiles);
1913 std::string tile;
1914 for(std::map<int, std::vector<std::string> >::reverse_iterator i = tiles.rbegin(); i != tiles.rend(); ++i) {
1915 if(i->second.empty() == false) {
1916 tile = i->second.back();
1917 std::cerr << "picking tile: '" << tile << "'\n";
1918 break;
1919 }
1920 }
1921
1922 if(!tile.empty()) {
1923 for(int n = 0; n != all_tilesets().size(); ++n) {
1924 if(all_tilesets()[n].type == tile) {
1925 tileset_dialog_->select_category(all_tilesets()[n].category);
1926 tileset_dialog_->set_tileset(n);
1927 std::cerr << "pick tile " << n << "\n";
1928 //if we're in adding objects mode then switch to adding tiles mode.
1929 if(tool_ == TOOL_ADD_OBJECT) {
1930 change_tool(TOOL_ADD_RECT);
1931 }
1932 return;
1933 }
1934 }
1935 }
1936 }
1937 } else if(editing_tiles() && !tile_selection_.empty() &&
1938 std::binary_search(tile_selection_.tiles.begin(), tile_selection_.tiles.end(), point(round_tile_size(anchorx_)/TileSize, round_tile_size(anchory_)/TileSize))) {
1939 //we are beginning to drag our selection
1940 dragging_ = true;
1941 } else if(tool() == TOOL_ADD_RECT || tool() == TOOL_SELECT_RECT) {
1942 tmp_undo_.reset();
1943 drawing_rect_ = true;
1944 g_rect_drawing = rect();
1945 } else if(tool() == TOOL_MAGIC_WAND) {
1946 drawing_rect_ = false;
1947 } else if(tool() == TOOL_PENCIL) {
1948 drawing_rect_ = false;
1949 dragging_ = true;
1950 point p(anchorx_, anchory_);
1951 if(buttons&SDL_BUTTON_LEFT) {
1952 add_tile_rect(p.x, p.y, p.x, p.y);
1953 } else {
1954 remove_tile_rect(p.x, p.y, p.x, p.y);
1955 }
1956 g_current_draw_tiles.clear();
1957 g_current_draw_tiles.push_back(p);
1958 } else if(tool() == TOOL_EDIT_HEXES) {
1959 drawing_rect_ = false;
1960 dragging_ = true;
1961 point p(anchorx_, anchory_);
1962 if(buttons&SDL_BUTTON_LEFT) {
1963 add_hex_tile_rect(p.x, p.y, p.x, p.y);
1964 } else {
1965 remove_hex_tile_rect(p.x, p.y, p.x, p.y);
1966 }
1967 g_current_draw_hex_tiles.clear();
1968 g_current_draw_hex_tiles.push_back(p);
1969 } else if(property_dialog_ && variable_info_selected(property_dialog_->get_entity(), anchorx_, anchory_, zoom_)) {
1970 g_variable_editing = variable_info_selected(property_dialog_->get_entity(), anchorx_, anchory_, zoom_, &g_variable_editing_index);
1971 g_variable_editing_original_value = property_dialog_->get_entity()->query_value(g_variable_editing->variable_name());
1972
1973 if(g_variable_editing->type() == editor_variable_info::TYPE_POINTS && event.button == SDL_BUTTON_RIGHT) {
1974 std::vector<variant> points = g_variable_editing_original_value.as_list();
1975 ASSERT_LOG(g_variable_editing_index >= 0 && g_variable_editing_index < points.size(), "INVALID VALUE WHEN EDITING POINTS: " << g_variable_editing_index << " / " << points.size());
1976
1977 points.erase(points.begin() + g_variable_editing_index);
1978
1979 variant new_value(&points);
1980
1981 std::vector<boost::function<void()> > undo, redo;
1982 generate_mutate_commands(property_dialog_->get_entity(), g_variable_editing->variable_name(), new_value, undo, redo);
1983 execute_command(
1984 boost::bind(execute_functions, redo),
1985 boost::bind(execute_functions, undo));
1986
1987 g_variable_editing = NULL;
1988 g_variable_editing_original_value = variant();
1989 g_variable_editing_index = -1;
1990 }
1991
1992 //If we select a variable to edit, return here so we don't select
1993 //another object instead, swallowing the event.
1994 return;
1995
1996 } else if(tool() == TOOL_SELECT_OBJECT && !lvl_->editor_highlight()) {
1997 //dragging a rectangle to select objects
1998 drawing_rect_ = true;
1999 } else if(property_dialog_) {
2000 property_dialog_->set_entity(lvl_->editor_highlight());
2001
2002 set_code_file();
2003 }
2004
2005 if(lvl_->editor_highlight()) {
2006 entity_ptr obj_selecting = lvl_->editor_highlight();
2007 if(std::count(lvl_->editor_selection().begin(),
2008 lvl_->editor_selection().end(), lvl_->editor_highlight()) == 0) {
2009 //set the object as selected in the editor.
2010 if(!shift_pressed) {
2011 lvl_->editor_clear_selection();
2012 }
2013
2014 obj_selecting = lvl_->editor_highlight();
2015 const bool ctrl_pressed = (SDL_GetModState()&(KMOD_LCTRL|KMOD_RCTRL)) != 0;
2016 while(!ctrl_pressed && obj_selecting->spawned_by().empty() == false && lvl_->get_entity_by_label(obj_selecting->spawned_by())) {
2017 obj_selecting = lvl_->get_entity_by_label(obj_selecting->spawned_by());
2018 }
2019
2020 lvl_->editor_select_object(obj_selecting);
2021
2022 property_dialog_->set_entity_group(lvl_->editor_selection());
2023
2024 if(!lvl_->editor_selection().empty() && tool() == TOOL_ADD_OBJECT) {
2025 //we are in add objects mode and we clicked on an object,
2026 //so change to select mode.
2027 change_tool(TOOL_SELECT_OBJECT);
2028 }
2029
2030 current_dialog_ = property_dialog_.get();
2031 } else if(shift_pressed) {
2032 lvl_->editor_deselect_object(lvl_->editor_highlight());
2033 }
2034
2035 //start dragging the object
2036 selected_entity_startx_ = obj_selecting->x();
2037 selected_entity_starty_ = obj_selecting->y();
2038
2039 g_started_dragging_object = false;
2040
2041 } else {
2042 //clear any selection in the editor
2043 lvl_->editor_clear_selection();
2044 }
2045
2046 if(tool() == TOOL_ADD_OBJECT && event.button == SDL_BUTTON_LEFT && !lvl_->editor_highlight()) {
2047 variant_builder node;
2048 node.merge_object(all_characters()[cur_object_].node);
2049 node.set("x", (ctrl_pressed ? anchorx_ : round_tile_size(anchorx_)));
2050 node.set("y", (ctrl_pressed ? anchory_ : round_tile_size(anchory_)));
2051 node.set("face_right", face_right_);
2052 node.set("upside_down", upside_down_);
2053
2054 if(custom_object_type::get(all_characters()[cur_object_].node["type"].as_string())->is_human()) {
2055 node.set("is_human", true);
2056 }
2057
2058 entity_ptr c(entity::build(node.build()));
2059
2060 //any vars that require formula initialization are calculated here.
2061 std::map<std::string, variant> vars;
2062 foreach(const editor_variable_info& info, c->editor_info()->vars()) {
2063 if(info.formula()) {
2064 vars[info.variable_name()] = info.formula()->execute(*c);
2065 }
2066 }
2067
2068 //if we have parallax, offset the object so it's placed at the same position it's graphically visible at
2069 c->set_x( c->x() + + ((1000 - (c->parallax_scale_millis_x()))* xpos_ )/1000);
2070 c->set_y( c->y() + + ((1000 - (c->parallax_scale_millis_y()))* ypos_ )/1000);
2071
2072
2073 //we only want to actually set the vars once we've calculated all of
2074 //them, to avoid any ordering issues etc. So set them all here.
2075 for(std::map<std::string, variant>::const_iterator i = vars.begin();
2076 i != vars.end(); ++i) {
2077 game_logic::formula_callable* obj_vars = c->query_value("vars").mutable_callable();
2078 obj_vars->mutate_value(i->first, i->second);
2079 }
2080
2081 if(!place_entity_in_level(*lvl_, *c)) {
2082 //could not place entity. Not really an error; the user just
2083 //clicked in an illegal position to place an object.
2084
2085 } else if(c->is_human() && lvl_->player()) {
2086 if(!shift_pressed) {
2087 begin_command_group();
2088 foreach(level_ptr lvl, levels_) {
2089 entity_ptr obj(c->backup());
2090 execute_command(
2091 boost::bind(&editor::add_object_to_level, this, lvl, obj),
2092 boost::bind(&editor::add_object_to_level, this, lvl, &lvl->player()->get_entity()));
2093 }
2094 end_command_group();
2095 } else {
2096 begin_command_group();
2097 foreach(level_ptr lvl, levels_) {
2098 entity_ptr obj(c->backup());
2099 execute_command(
2100 boost::bind(&editor::add_multi_object_to_level, this, lvl, obj),
2101 boost::bind(&editor::add_object_to_level, this, lvl, &lvl->player()->get_entity()));
2102 }
2103 end_command_group();
2104 }
2105
2106 } else {
2107 begin_command_group();
2108 foreach(level_ptr lvl, levels_) {
2109 entity_ptr obj(c->backup());
2110 execute_command(
2111 boost::bind(&editor::add_object_to_level, this, lvl, obj),
2112 boost::bind(&editor::remove_object_from_level, this, lvl, obj));
2113 std::cerr << "ADD OBJECT: " << obj->x() << "," << obj->y() << "\n";
2114 }
2115 end_command_group();
2116 }
2117 }
2118 }
2119
handle_mouse_button_up(const SDL_MouseButtonEvent & event)2120 void editor::handle_mouse_button_up(const SDL_MouseButtonEvent& event)
2121 {
2122 const bool ctrl_pressed = (SDL_GetModState()&(KMOD_LCTRL|KMOD_RCTRL)) != 0;
2123 const bool shift_pressed = (SDL_GetModState()&(KMOD_LSHIFT|KMOD_RSHIFT)) != 0;
2124 int mousex, mousey;
2125 const unsigned int buttons = get_mouse_state(mousex, mousey);
2126
2127 const int xpos = xpos_ + mousex*zoom_;
2128 const int ypos = ypos_ + mousey*zoom_;
2129
2130 if((event.button == SDL_BUTTON_WHEELUP || event.button == SDL_BUTTON_WHEELDOWN)
2131 && xpos < editor_x_resolution-sidebar_width() ) {
2132 if(event.button == SDL_BUTTON_WHEELUP) {
2133 zoom_in();
2134 } else {
2135 zoom_out();
2136 }
2137 return;
2138 }
2139
2140 if(g_variable_editing) {
2141 if(property_dialog_ && property_dialog_->get_entity()) {
2142 entity_ptr e = property_dialog_->get_entity();
2143 const std::string& var = g_variable_editing->variable_name();
2144
2145 begin_command_group();
2146 foreach(level_ptr lvl, levels_) {
2147 entity_ptr obj = lvl->get_entity_by_label(e->label());
2148 if(obj) {
2149 execute_command(
2150 boost::bind(&editor::mutate_object_value, this, lvl, obj.get(), var, e->query_value(var)),
2151 boost::bind(&editor::mutate_object_value, this, lvl, obj.get(), var, g_variable_editing_original_value));
2152 }
2153 }
2154 end_command_group();
2155 property_dialog_->init();
2156 }
2157 g_variable_editing = NULL;
2158 return;
2159 }
2160
2161 if(resizing_left_level_edge || resizing_right_level_edge ||resizing_top_level_edge || resizing_bottom_level_edge) {
2162 rect boundaries = modify_selected_rect(*this, lvl_->boundaries(), xpos, ypos);
2163
2164 resizing_left_level_edge = resizing_right_level_edge = resizing_top_level_edge = resizing_bottom_level_edge = false;
2165
2166 if(boundaries != lvl_->boundaries()) {
2167 begin_command_group();
2168 foreach(level_ptr lvl, levels_) {
2169 execute_command(
2170 boost::bind(&level::set_boundaries, lvl.get(), boundaries),
2171 boost::bind(&level::set_boundaries, lvl.get(), lvl->boundaries()));
2172 }
2173 end_command_group();
2174 }
2175 return;
2176 }
2177
2178
2179 if(editing_tiles()) {
2180 if(dragging_) {
2181 const int selectx = xpos_ + mousex*zoom_;
2182 const int selecty = ypos_ + mousey*zoom_;
2183
2184 //dragging selection
2185 int diffx = (selectx - anchorx_)/TileSize;
2186 int diffy = (selecty - anchory_)/TileSize;
2187
2188 std::cerr << "MAKE DIFF: " << diffx << "," << diffy << "\n";
2189 std::vector<boost::function<void()> > redo, undo;
2190
2191 foreach(level_ptr lvl, levels_) {
2192 foreach(const point& p, tile_selection_.tiles) {
2193 const int x = (p.x+diffx)*TileSize;
2194 const int y = (p.y+diffy)*TileSize;
2195 undo.push_back(boost::bind(&level::clear_tile_rect,lvl.get(), x, y, x, y));
2196 }
2197
2198 int min_x = INT_MAX, min_y = INT_MAX, max_x = INT_MIN, max_y = INT_MIN;
2199
2200 //backup both the contents of the old and new regions, so we can restore them both
2201 foreach(const point& p, tile_selection_.tiles) {
2202 int x = p.x*TileSize;
2203 int y = p.y*TileSize;
2204
2205 min_x = std::min(x, min_x);
2206 max_x = std::max(x, max_x);
2207 min_y = std::min(y, min_y);
2208 max_y = std::max(y, max_y);
2209
2210 std::map<int, std::vector<std::string> > old_tiles;
2211 lvl->get_all_tiles_rect(x, y, x, y, old_tiles);
2212 for(std::map<int, std::vector<std::string> >::const_iterator i = old_tiles.begin(); i != old_tiles.end(); ++i) {
2213 undo.push_back(boost::bind(&level::add_tile_rect_vector, lvl.get(), i->first, x, y, x, y, i->second));
2214 redo.push_back(boost::bind(&level::add_tile_rect_vector, lvl.get(), i->first, x, y, x, y, std::vector<std::string>(1,"")));
2215 }
2216
2217 old_tiles.clear();
2218
2219 x += diffx*TileSize;
2220 y += diffy*TileSize;
2221
2222 min_x = std::min(x, min_x);
2223 max_x = std::max(x, max_x);
2224 min_y = std::min(y, min_y);
2225 max_y = std::max(y, max_y);
2226
2227 lvl->get_all_tiles_rect(x, y, x, y, old_tiles);
2228 for(std::map<int, std::vector<std::string> >::const_iterator i = old_tiles.begin(); i != old_tiles.end(); ++i) {
2229 undo.push_back(boost::bind(&level::add_tile_rect_vector, lvl.get(), i->first, x, y, x, y, i->second));
2230 redo.push_back(boost::bind(&level::add_tile_rect_vector, lvl.get(), i->first, x, y, x, y, std::vector<std::string>(1,"")));
2231 }
2232 }
2233
2234
2235 foreach(const point& p, tile_selection_.tiles) {
2236 const int x = p.x*TileSize;
2237 const int y = p.y*TileSize;
2238
2239 min_x = std::min(x + diffx*TileSize, min_x);
2240 max_x = std::max(x + diffx*TileSize, max_x);
2241 min_y = std::min(y + diffy*TileSize, min_y);
2242 max_y = std::max(y + diffy*TileSize, max_y);
2243
2244 std::map<int, std::vector<std::string> > old_tiles;
2245 lvl->get_all_tiles_rect(x, y, x, y, old_tiles);
2246 for(std::map<int, std::vector<std::string> >::const_iterator i = old_tiles.begin(); i != old_tiles.end(); ++i) {
2247 redo.push_back(boost::bind(&level::add_tile_rect_vector, lvl.get(), i->first, x + diffx*TileSize, y + diffy*TileSize, x + diffx*TileSize, y + diffy*TileSize, i->second));
2248 }
2249 }
2250
2251 if(!tile_selection_.tiles.empty()) {
2252 undo.push_back(boost::bind(&level::start_rebuild_tiles_in_background, lvl.get(), std::vector<int>()));
2253 redo.push_back(boost::bind(&level::start_rebuild_tiles_in_background, lvl.get(), std::vector<int>()));
2254 }
2255 }
2256
2257 tile_selection new_selection = tile_selection_;
2258 foreach(point& p, new_selection.tiles) {
2259 p.x += diffx;
2260 p.y += diffy;
2261 }
2262
2263 redo.push_back(boost::bind(&editor::set_selection, this, new_selection));
2264 undo.push_back(boost::bind(&editor::set_selection, this, tile_selection_));
2265
2266 execute_command(
2267 boost::bind(execute_functions, redo),
2268 boost::bind(execute_functions, undo));
2269
2270 } else if(!drawing_rect_) {
2271 //wasn't drawing a rect.
2272 if(event.button == SDL_BUTTON_LEFT && tool() == TOOL_MAGIC_WAND) {
2273 select_magic_wand(anchorx_, anchory_);
2274 }
2275 } else if(event.button == SDL_BUTTON_LEFT) {
2276
2277 if(tool() == TOOL_ADD_RECT) {
2278
2279 lvl_->freeze_rebuild_tiles_in_background();
2280 if(tmp_undo_.get()) {
2281 //if we have a temporary change that was made while dragging
2282 //to preview the change, undo that now.
2283 tmp_undo_->undo_command();
2284 tmp_undo_.reset();
2285 }
2286
2287 add_tile_rect(anchorx_, anchory_, xpos, ypos);
2288 lvl_->unfreeze_rebuild_tiles_in_background();
2289 } else if(tool() == TOOL_SELECT_RECT) {
2290 select_tile_rect(anchorx_, anchory_, xpos, ypos);
2291 }
2292
2293 } else if(event.button == SDL_BUTTON_RIGHT) {
2294 lvl_->freeze_rebuild_tiles_in_background();
2295 if(tmp_undo_.get()) {
2296 //if we have a temporary change that was made while dragging
2297 //to preview the change, undo that now.
2298 tmp_undo_->undo_command();
2299 tmp_undo_.reset();
2300 }
2301 remove_tile_rect(anchorx_, anchory_, xpos, ypos);
2302 lvl_->unfreeze_rebuild_tiles_in_background();
2303 }
2304 } else {
2305 //some kind of object editing
2306 if(event.button == SDL_BUTTON_RIGHT) {
2307 std::vector<boost::function<void()> > undo, redo;
2308 const rect rect_selected(rect::from_coordinates(anchorx_, anchory_, xpos, ypos));
2309 std::vector<entity_ptr> chars = lvl_->get_characters_in_rect(rect_selected, xpos_, ypos_);
2310
2311 //Delete all the objects in the rect.
2312 foreach(const entity_ptr& c, chars) {
2313 if(c->spawned_by().empty() == false) {
2314 continue;
2315 }
2316 std::cerr << "REMOVING RECT CHAR: " << c->debug_description() << "\n";
2317 foreach(level_ptr lvl, levels_) {
2318 entity_ptr obj = lvl->get_entity_by_label(c->label());
2319 generate_remove_commands(obj, undo, redo);
2320 }
2321 }
2322
2323 if(property_dialog_ && property_dialog_.get() == current_dialog_ && property_dialog_->get_entity() && property_dialog_->get_entity()->editor_info()) {
2324 //As well as removing objects, we will remove any vertices
2325 //that we see.
2326 foreach(const editor_variable_info& var, property_dialog_->get_entity()->editor_info()->vars()) {
2327 const std::string& name = var.variable_name();
2328 const editor_variable_info::VARIABLE_TYPE type = var.type();
2329 if(type != editor_variable_info::TYPE_POINTS) {
2330 continue;
2331 }
2332
2333 variant value = property_dialog_->get_entity()->query_value(name);
2334 if(!value.is_list()) {
2335 continue;
2336 }
2337
2338 std::vector<point> points;
2339 foreach(const variant& v, value.as_list()) {
2340 points.push_back(point(v));
2341 }
2342
2343 bool modified = false;
2344 for(std::vector<point>::iterator i = points.begin(); i != points.end(); ) {
2345 if(point_in_rect(*i, rect_selected)) {
2346 modified = true;
2347 i = points.erase(i);
2348 } else {
2349 ++i;
2350 }
2351 }
2352
2353 if(modified) {
2354 std::vector<variant> points_var;
2355 foreach(const point& p, points) {
2356 points_var.push_back(p.write());
2357 }
2358
2359 generate_mutate_commands(property_dialog_->get_entity(), name, variant(&points_var), undo, redo);
2360 }
2361 }
2362 }
2363
2364 execute_command(
2365 boost::bind(execute_functions, redo),
2366 boost::bind(execute_functions, undo));
2367 } else if(tool() == TOOL_SELECT_OBJECT && drawing_rect_) {
2368 std::vector<entity_ptr> chars = lvl_->get_characters_in_rect(rect::from_coordinates(anchorx_, anchory_, xpos, ypos), xpos_, ypos_);
2369 if(chars.empty()) {
2370 //no chars is just a no-op.
2371 drawing_rect_ = dragging_ = false;
2372 return;
2373 }
2374
2375 const bool ctrl_pressed = (SDL_GetModState()&(KMOD_LCTRL|KMOD_RCTRL)) != 0;
2376 foreach(const entity_ptr& c, chars) {
2377 if(c->spawned_by().empty() || ctrl_pressed) {
2378 lvl_->editor_select_object(c);
2379 }
2380 }
2381
2382 property_dialog_->set_entity_group(lvl_->editor_selection());
2383
2384 if(lvl_->editor_selection().size() == 1) {
2385 current_dialog_ = property_dialog_.get();
2386 property_dialog_->set_entity(lvl_->editor_selection().front());
2387
2388 set_code_file();
2389 } else {
2390 current_dialog_ = property_dialog_.get();
2391 }
2392 }
2393 }
2394
2395 drawing_rect_ = dragging_ = false;
2396 }
2397
2398
load_stats()2399 void editor::load_stats()
2400 {
2401 }
2402
show_stats()2403 void editor::show_stats()
2404 {
2405 editor_dialogs::editor_stats_dialog stats_dialog(*this);
2406 stats_dialog.show_modal();
2407 }
2408
download_stats()2409 void editor::download_stats()
2410 {
2411 const bool result = stats::download(lvl_->id());
2412 if(result) {
2413 debug_console::add_message("Got latest stats from the server");
2414 try {
2415 load_stats();
2416 } catch(...) {
2417 debug_console::add_message("Error parsing stats");
2418 std::cerr << "ERROR LOADING STATS\n";
2419 }
2420 } else {
2421 debug_console::add_message("Download of stats failed");
2422 }
2423 }
2424
get_tile_zorder(const std::string & tile_id) const2425 int editor::get_tile_zorder(const std::string& tile_id) const
2426 {
2427 foreach(const editor::tileset& tile, tilesets) {
2428 if(tile.type == tile_id) {
2429 return tile.zorder;
2430 }
2431 }
2432
2433 return 0;
2434 }
2435
add_tile_rect(int zorder,const std::string & tile_id,int x1,int y1,int x2,int y2)2436 void editor::add_tile_rect(int zorder, const std::string& tile_id, int x1, int y1, int x2, int y2)
2437 {
2438 if(x2 < x1) {
2439 std::swap(x1, x2);
2440 }
2441
2442 if(y2 < y1) {
2443 std::swap(y1, y2);
2444 }
2445
2446 std::vector<boost::function<void()> > undo, redo;
2447
2448 foreach(level_ptr lvl, levels_) {
2449 std::vector<std::string> old_rect;
2450 lvl->get_tile_rect(zorder, x1, y1, x2, y2, old_rect);
2451
2452 redo.push_back(boost::bind(&level::add_tile_rect, lvl.get(), zorder, x1, y1, x2, y2, tile_id));
2453 undo.push_back(boost::bind(&level::add_tile_rect_vector, lvl.get(), zorder, x1, y1, x2, y2, old_rect));
2454
2455 std::vector<int> layers;
2456 layers.push_back(zorder);
2457 undo.push_back(boost::bind(&level::start_rebuild_tiles_in_background, lvl.get(), layers));
2458 redo.push_back(boost::bind(&level::start_rebuild_tiles_in_background, lvl.get(), layers));
2459 }
2460
2461 execute_command(
2462 boost::bind(execute_functions, redo),
2463 boost::bind(execute_functions, undo));
2464
2465 if(layers_dialog_) {
2466 layers_dialog_->init();
2467 }
2468 }
2469
add_tile_rect(int x1,int y1,int x2,int y2)2470 void editor::add_tile_rect(int x1, int y1, int x2, int y2)
2471 {
2472 x1 += ((100 - tilesets[cur_tileset_].x_speed)*xpos_)/100;
2473 x2 += ((100 - tilesets[cur_tileset_].x_speed)*xpos_)/100;
2474 y1 += ((100 - tilesets[cur_tileset_].y_speed)*ypos_)/100;
2475 y2 += ((100 - tilesets[cur_tileset_].y_speed)*ypos_)/100;
2476
2477 add_tile_rect(tilesets[cur_tileset_].zorder, tilesets[cur_tileset_].type, x1, y1, x2, y2);
2478 foreach(level_ptr lvl, levels_) {
2479 lvl->set_tile_layer_speed(tilesets[cur_tileset_].zorder,
2480 tilesets[cur_tileset_].x_speed,
2481 tilesets[cur_tileset_].y_speed);
2482 }
2483 }
2484
remove_tile_rect(int x1,int y1,int x2,int y2)2485 void editor::remove_tile_rect(int x1, int y1, int x2, int y2)
2486 {
2487 x1 += ((100 - tilesets[cur_tileset_].x_speed)*xpos_)/100;
2488 x2 += ((100 - tilesets[cur_tileset_].x_speed)*xpos_)/100;
2489 y1 += ((100 - tilesets[cur_tileset_].y_speed)*ypos_)/100;
2490 y2 += ((100 - tilesets[cur_tileset_].y_speed)*ypos_)/100;
2491
2492 if(x2 < x1) {
2493 std::swap(x1, x2);
2494 }
2495
2496 if(y2 < y1) {
2497 std::swap(y1, y2);
2498 }
2499
2500 std::vector<boost::function<void()> > redo, undo;
2501 foreach(level_ptr lvl, levels_) {
2502
2503 std::map<int, std::vector<std::string> > old_tiles;
2504 lvl->get_all_tiles_rect(x1, y1, x2, y2, old_tiles);
2505 for(std::map<int, std::vector<std::string> >::const_iterator i = old_tiles.begin(); i != old_tiles.end(); ++i) {
2506 undo.push_back(boost::bind(&level::add_tile_rect_vector, lvl.get(), i->first, x1, y1, x2, y2, i->second));
2507 }
2508
2509 redo.push_back(boost::bind(&level::clear_tile_rect, lvl.get(), x1, y1, x2, y2));
2510 undo.push_back(boost::bind(&level::start_rebuild_tiles_in_background, lvl.get(), std::vector<int>()));
2511 redo.push_back(boost::bind(&level::start_rebuild_tiles_in_background, lvl.get(), std::vector<int>()));
2512 }
2513
2514 execute_command(
2515 boost::bind(execute_functions, redo),
2516 boost::bind(execute_functions, undo));
2517 }
2518
select_tile_rect(int x1,int y1,int x2,int y2)2519 void editor::select_tile_rect(int x1, int y1, int x2, int y2)
2520 {
2521 tile_selection new_selection;
2522
2523 const bool shift_pressed = (SDL_GetModState()&KMOD_SHIFT) != 0;
2524 if(shift_pressed) {
2525 //adding to the selection
2526 new_selection = tile_selection_;
2527 }
2528
2529 if(x2 < x1) {
2530 std::swap(x1, x2);
2531 }
2532
2533 if(y2 < y1) {
2534 std::swap(y1, y2);
2535 }
2536
2537 if(x2 - x1 > TileSize/4 || y2 - y1 > TileSize/4) {
2538 x2 += TileSize;
2539 y2 += TileSize;
2540
2541 x1 = round_tile_size(x1)/TileSize;
2542 y1 = round_tile_size(y1)/TileSize;
2543 x2 = round_tile_size(x2)/TileSize;
2544 y2 = round_tile_size(y2)/TileSize;
2545
2546 for(int x = x1; x != x2; ++x) {
2547 for(int y = y1; y != y2; ++y) {
2548 const point p(x, y);
2549 new_selection.tiles.push_back(p);
2550 }
2551 }
2552
2553 std::sort(new_selection.tiles.begin(), new_selection.tiles.end());
2554
2555 const bool alt_pressed = (SDL_GetModState()&(KMOD_LALT|KMOD_RALT)) != 0;
2556 if(alt_pressed) {
2557 //diff from selection
2558 tile_selection diff;
2559 foreach(const point& p, tile_selection_.tiles) {
2560 if(std::binary_search(new_selection.tiles.begin(), new_selection.tiles.end(), p) == false) {
2561 diff.tiles.push_back(p);
2562 }
2563 }
2564
2565 new_selection.tiles.swap(diff.tiles);
2566 }
2567 }
2568
2569 execute_command(
2570 boost::bind(&editor::set_selection, this, new_selection),
2571 boost::bind(&editor::set_selection, this, tile_selection_));
2572 }
2573
add_hex_tile_rect(int x1,int y1,int x2,int y2)2574 void editor::add_hex_tile_rect(int x1, int y1, int x2, int y2)
2575 {
2576 if(x2 < x1) {
2577 std::swap(x1, x2);
2578 }
2579
2580 if(y2 < y1) {
2581 std::swap(y1, y2);
2582 }
2583
2584 // fudge
2585 const int zorder = -1000;
2586 std::vector<hex::hex_tile_ptr>& t = hex::hex_object::get_editor_tiles();
2587
2588 std::vector<boost::function<void()> > undo, redo;
2589
2590 foreach(level_ptr lvl, levels_) {
2591 std::vector<std::string> old_rect;
2592 lvl->get_hex_tile_rect(zorder, x1, y1, x2, y2, old_rect);
2593
2594 redo.push_back(boost::bind(&level::add_hex_tile_rect, lvl.get(), zorder, x1, y1, x2, y2, t[get_hex_tileset()]->get_editor_info().type));
2595 undo.push_back(boost::bind(&level::add_hex_tile_rect_vector, lvl.get(), zorder, x1, y1, x2, y2, old_rect));
2596
2597 std::vector<int> layers;
2598 layers.push_back(zorder);
2599 undo.push_back(boost::bind(&level::start_rebuild_hex_tiles_in_background, lvl.get(), layers));
2600 redo.push_back(boost::bind(&level::start_rebuild_hex_tiles_in_background, lvl.get(), layers));
2601 }
2602
2603 execute_command(
2604 boost::bind(execute_functions, redo),
2605 boost::bind(execute_functions, undo));
2606
2607 if(layers_dialog_) {
2608 layers_dialog_->init();
2609 }
2610 }
2611
remove_hex_tile_rect(int x1,int y1,int x2,int y2)2612 void editor::remove_hex_tile_rect(int x1, int y1, int x2, int y2)
2613 {
2614 if(x2 < x1) {
2615 std::swap(x1, x2);
2616 }
2617
2618 if(y2 < y1) {
2619 std::swap(y1, y2);
2620 }
2621
2622 std::vector<boost::function<void()> > redo, undo;
2623 foreach(level_ptr lvl, levels_) {
2624
2625 std::map<int, std::vector<std::string> > old_tiles;
2626 lvl->get_all_hex_tiles_rect(x1, y1, x2, y2, old_tiles);
2627 for(std::map<int, std::vector<std::string> >::const_iterator i = old_tiles.begin(); i != old_tiles.end(); ++i) {
2628 undo.push_back(boost::bind(&level::add_hex_tile_rect_vector, lvl.get(), i->first, x1, y1, x2, y2, i->second));
2629 }
2630
2631 redo.push_back(boost::bind(&level::clear_hex_tile_rect, lvl.get(), x1, y1, x2, y2));
2632 undo.push_back(boost::bind(&level::start_rebuild_tiles_in_background, lvl.get(), std::vector<int>()));
2633 redo.push_back(boost::bind(&level::start_rebuild_tiles_in_background, lvl.get(), std::vector<int>()));
2634 }
2635
2636 execute_command(
2637 boost::bind(execute_functions, redo),
2638 boost::bind(execute_functions, undo));
2639 }
2640
select_magic_wand(int xpos,int ypos)2641 void editor::select_magic_wand(int xpos, int ypos)
2642 {
2643 tile_selection new_selection;
2644
2645 const bool ctrl_pressed = (SDL_GetModState()&(KMOD_LCTRL|KMOD_RCTRL)) != 0;
2646 if(ctrl_pressed) {
2647 //adding to the selection
2648 new_selection = tile_selection_;
2649 }
2650
2651 std::vector<point> tiles = lvl_->get_solid_contiguous_region(xpos, ypos);
2652 new_selection.tiles.insert(new_selection.tiles.end(), tiles.begin(), tiles.end());
2653 execute_command(
2654 boost::bind(&editor::set_selection, this, new_selection),
2655 boost::bind(&editor::set_selection, this, tile_selection_));
2656 }
2657
set_selection(const tile_selection & s)2658 void editor::set_selection(const tile_selection& s)
2659 {
2660 tile_selection_ = s;
2661 }
2662
move_object(level_ptr lvl,entity_ptr e,int new_x,int new_y)2663 void editor::move_object(level_ptr lvl, entity_ptr e, int new_x, int new_y)
2664 {
2665 current_level_scope scope(lvl.get());
2666 lvl->relocate_object(e, new_x, new_y);
2667 }
2668
all_tilesets() const2669 const std::vector<editor::tileset>& editor::all_tilesets() const
2670 {
2671 return tilesets;
2672 }
2673
all_characters() const2674 std::vector<editor::enemy_type>& editor::all_characters() const
2675 {
2676 if(enemy_types.empty()) {
2677 typedef std::pair<std::string, custom_object_type::EditorSummary> type_cat;
2678 foreach(const type_cat& item, custom_object_type::get_editor_categories()) {
2679 enemy_types.push_back(enemy_type(item.first, item.second.category, item.second.first_frame));
2680 enemy_types.back().help = item.second.help;
2681 }
2682 }
2683
2684 return enemy_types;
2685 }
2686
set_tileset(int index)2687 void editor::set_tileset(int index)
2688 {
2689 cur_tileset_ = index;
2690 if(cur_tileset_ < 0) {
2691 cur_tileset_ = tilesets.size()-1;
2692 } else if(cur_tileset_ >= tilesets.size()) {
2693 cur_tileset_ = 0;
2694 }
2695
2696 foreach(level_ptr lvl, levels_) {
2697 lvl->set_tile_layer_speed(tilesets[cur_tileset_].zorder,
2698 tilesets[cur_tileset_].x_speed,
2699 tilesets[cur_tileset_].y_speed);
2700 }
2701 }
2702
set_hex_tileset(int index)2703 void editor::set_hex_tileset(int index)
2704 {
2705 cur_hex_tileset_ = index;
2706 if(cur_hex_tileset_ < 0) {
2707 cur_hex_tileset_ = hex::hex_object::get_hex_tiles().size()-1;
2708 } else if(cur_hex_tileset_ >= hex::hex_object::get_hex_tiles().size()) {
2709 cur_hex_tileset_ = 0;
2710 }
2711 }
2712
set_object(int index)2713 void editor::set_object(int index)
2714 {
2715 int max = all_characters().size();
2716
2717 if(index < 0) {
2718 index = max - 1;
2719 } else if(index >= max) {
2720 index = 0;
2721 }
2722
2723 cur_object_ = index;
2724 }
2725
tool() const2726 editor::EDIT_TOOL editor::tool() const
2727 {
2728 const bool alt_pressed = (SDL_GetModState()&KMOD_ALT) != 0;
2729 if(alt_pressed) {
2730 switch(tool_) {
2731 case TOOL_ADD_OBJECT:
2732 case TOOL_ADD_RECT:
2733 case TOOL_SELECT_RECT:
2734 case TOOL_MAGIC_WAND:
2735 case TOOL_PENCIL:
2736 case TOOL_PICKER:
2737 return TOOL_PICKER;
2738 default:
2739 break;
2740 }
2741 }
2742
2743 return tool_;
2744 }
2745
change_tool(EDIT_TOOL tool)2746 void editor::change_tool(EDIT_TOOL tool)
2747 {
2748 EDIT_TOOL last_tool = tool_;
2749 tool_ = tool;
2750 selected_segment_ = -1;
2751
2752 std::cerr << "CHANGE TOOL: " << (int)tool << "\n";
2753
2754 switch(tool_) {
2755 case TOOL_ADD_RECT:
2756 case TOOL_SELECT_RECT:
2757 case TOOL_MAGIC_WAND:
2758 case TOOL_PENCIL:
2759 case TOOL_PICKER: {
2760 if(!tileset_dialog_) {
2761 tileset_dialog_.reset(new editor_dialogs::tileset_editor_dialog(*this));
2762 }
2763 current_dialog_ = tileset_dialog_.get();
2764 lvl_->editor_clear_selection();
2765 break;
2766 }
2767 case TOOL_ADD_OBJECT: {
2768 if(!character_dialog_) {
2769 character_dialog_.reset(new editor_dialogs::character_editor_dialog(*this));
2770 }
2771 current_dialog_ = character_dialog_.get();
2772 character_dialog_->set_character(cur_object_);
2773 break;
2774 }
2775 case TOOL_SELECT_OBJECT: {
2776 current_dialog_ = property_dialog_.get();
2777 break;
2778 }
2779 case TOOL_EDIT_SEGMENTS: {
2780
2781 if(!segment_dialog_) {
2782 segment_dialog_.reset(new editor_dialogs::segment_editor_dialog(*this));
2783 }
2784
2785 current_dialog_ = segment_dialog_.get();
2786 segment_dialog_->set_segment(selected_segment_);
2787 break;
2788 }
2789 case TOOL_EDIT_HEXES: {
2790 if(hex::hex_object::get_hex_tiles().size() > 0) {
2791 if(!hex_tileset_dialog_) {
2792 hex_tileset_dialog_.reset(new editor_dialogs::hex_tileset_editor_dialog(*this));
2793 }
2794 current_dialog_ = hex_tileset_dialog_.get();
2795 lvl_->editor_clear_selection();
2796 } else {
2797 tool_ = last_tool;
2798 debug_console::add_message("There isn't a hex tile definition file or file is empty/invalid!");
2799 return;
2800 }
2801 break;
2802 }
2803 default: {
2804 break;
2805 }
2806 }
2807
2808 if(editor_mode_dialog_) {
2809 editor_mode_dialog_->init();
2810 }
2811
2812 reset_dialog_positions();
2813 }
2814
save_level_as(const std::string & fname)2815 void editor::save_level_as(const std::string& fname)
2816 {
2817 const std::string id = module::make_module_id(fname);
2818 all_editors.erase(filename_);
2819 all_editors[id] = this;
2820
2821 std::string path = module::get_id(fname);
2822 std::string modname = module::get_module_id(fname);
2823 sys::write_file(module::get_module_path(modname, preferences::editor_save_to_user_preferences() ? module::BASE_PATH_USER : module::BASE_PATH_GAME) + preferences::level_path() + path, "");
2824 loadlevel::load_level_paths();
2825 filename_ = id;
2826 save_level();
2827 g_last_edited_level() = id;
2828 }
2829
quit()2830 void editor::quit()
2831 {
2832 if(confirm_quit()) {
2833 done_ = true;
2834 }
2835 }
2836
2837 namespace {
quit_editor_result(gui::dialog * d,int * result_ptr,int result)2838 void quit_editor_result(gui::dialog* d, int* result_ptr, int result) {
2839 d->close();
2840 *result_ptr = result;
2841 }
2842 }
2843
confirm_quit(bool allow_cancel)2844 bool editor::confirm_quit(bool allow_cancel)
2845 {
2846 if(!level_changed_) {
2847 return true;
2848 }
2849
2850 const int center_x = graphics::screen_width()/2;
2851 const int center_y = graphics::screen_height()/2;
2852 using namespace gui;
2853 dialog d(center_x - 140, center_y - 100, center_x + 140, center_y + 100);
2854
2855 d.add_widget(widget_ptr(new label("Do you want to save the level?", graphics::color_white())), dialog::MOVE_DOWN);
2856
2857 gui::grid* grid = new gui::grid(allow_cancel ? 3 : 2);
2858
2859 int result = 0;
2860 grid->add_col(widget_ptr(
2861 new button(widget_ptr(new label("Yes", graphics::color_white())),
2862 boost::bind(quit_editor_result, &d, &result, 0))));
2863 grid->add_col(widget_ptr(
2864 new button(widget_ptr(new label("No", graphics::color_white())),
2865 boost::bind(quit_editor_result, &d, &result, 1))));
2866 if(allow_cancel) {
2867 grid->add_col(widget_ptr(
2868 new button(widget_ptr(new label("Cancel", graphics::color_white())),
2869 boost::bind(quit_editor_result, &d, &result, 2))));
2870 }
2871 d.add_widget(widget_ptr(grid));
2872 d.show_modal();
2873
2874 if(result == 2) {
2875 return false;
2876 }
2877
2878 if(result == 0 && !d.cancelled()) {
2879 save_level();
2880 }
2881
2882 return true;
2883 }
2884
save_level()2885 void editor::save_level()
2886 {
2887 controls::control_backup_scope ctrl_backup;
2888
2889 toggle_active_level();
2890
2891 lvl_->set_id(filename_);
2892
2893 level_changed_ = 0;
2894
2895 remove_ghost_objects();
2896 ghost_objects_.clear();
2897
2898 std::string data;
2899 variant lvl_node = lvl_->write();
2900 std::map<variant,variant> attr = lvl_node.as_map();
2901 attr.erase(variant("cycle")); //levels saved in the editor should never
2902 //have a cycle attached to them so that
2903 //all levels start at cycle 0.
2904 lvl_node = variant(&attr);
2905 std::cerr << "GET LEVEL FILENAME: " << filename_ << "\n";
2906 if(preferences::is_level_path_set()) {
2907 sys::write_file(preferences::level_path() + filename_, lvl_node.write_json(true));
2908 } else {
2909 std::string path = loadlevel::get_level_path(filename_);
2910 if(preferences::editor_save_to_user_preferences()) {
2911 path = module::get_module_path(module::get_module_name(), module::BASE_PATH_USER) + "/data/level/" + filename_;
2912 }
2913
2914 std::cerr << "WRITE_LEVEL: " << path << "\n";
2915 sys::write_file(path, lvl_node.write_json(true));
2916 }
2917
2918 //see if we should write the next/previous levels also
2919 //based on them having changed.
2920 if(lvl_->previous_level().empty() == false) {
2921 try {
2922 level_ptr prev(new level(lvl_->previous_level()));
2923 prev->finish_loading();
2924 if(prev->next_level() != lvl_->id()) {
2925 prev->set_next_level(lvl_->id());
2926 if(preferences::is_level_path_set()) {
2927 sys::write_file(preferences::level_path() + prev->id(), prev->write().write_json(true));
2928 } else if(preferences::editor_save_to_user_preferences()) {
2929 sys::write_file(module::get_module_path(module::get_module_name(), module::BASE_PATH_USER) + "/data/level/" + prev->id(), prev->write().write_json(true));
2930 } else {
2931 sys::write_file(module::map_file(prev->id()), prev->write().write_json(true));
2932 }
2933 }
2934 } catch(...) {
2935 }
2936 }
2937
2938 if(lvl_->next_level().empty() == false) {
2939 try {
2940 level_ptr next(new level(lvl_->next_level()));
2941 next->finish_loading();
2942 if(next->previous_level() != lvl_->id()) {
2943 next->set_previous_level(lvl_->id());
2944 if(preferences::is_level_path_set()) {
2945 sys::write_file(preferences::level_path() + next->id(), next->write().write_json(true));
2946 } else if(preferences::editor_save_to_user_preferences()) {
2947 sys::write_file(module::get_module_path("", module::BASE_PATH_USER) + "/data/level/" + next->id(), next->write().write_json(true));
2948 } else {
2949 sys::write_file(module::map_file(next->id()), next->write().write_json(true));
2950 }
2951 }
2952 } catch(...) {
2953 }
2954 }
2955
2956 toggle_active_level();
2957 }
2958
zoom_in()2959 void editor::zoom_in()
2960 {
2961 if(zoom_ > 1) {
2962 zoom_ /= 2;
2963 }
2964 }
2965
zoom_out()2966 void editor::zoom_out()
2967 {
2968 if(zoom_ < 8) {
2969 zoom_ *= 2;
2970 }
2971 }
2972
draw() const2973 void editor::draw() const
2974 {
2975 graphics::prepare_raster();
2976 glClearColor(0.0, 0.0, 0.0, 0.0);
2977 glClear(GL_COLOR_BUFFER_BIT);
2978
2979 if(zoom_ == 1) {
2980 //backgrounds only draw nicely at the regular zoom level for now.
2981 lvl_->draw_background(xpos_, ypos_, 0);
2982 }
2983
2984 lvl_->draw(xpos_, ypos_, graphics::screen_width()*zoom_, graphics::screen_height()*zoom_);
2985
2986 draw_gui();
2987
2988 debug_console::draw();
2989
2990 SDL_GL_SwapBuffers();
2991 }
2992
draw_gui() const2993 void editor::draw_gui() const
2994 {
2995 glPushMatrix();
2996 glScalef(1.0/zoom_, 1.0/zoom_, 0);
2997 glTranslatef(-xpos_,-ypos_,0);
2998
2999 const bool ctrl_pressed = (SDL_GetModState()&(KMOD_LCTRL|KMOD_RCTRL)) != 0;
3000 int mousex, mousey;
3001 get_mouse_state(mousex, mousey);
3002 const int selectx = xpos_ + mousex*zoom_;
3003 const int selecty = ypos_ + mousey*zoom_;
3004
3005 {
3006 std::string next_level = "To " + lvl_->next_level();
3007 std::string previous_level = "To " + lvl_->previous_level();
3008 if(lvl_->next_level().empty()) {
3009 next_level = "(no next level)";
3010 }
3011 if(lvl_->previous_level().empty()) {
3012 previous_level = "(no previous level)";
3013 }
3014 graphics::texture t = font::render_text(previous_level, graphics::color_black(), 24);
3015 int x = lvl_->boundaries().x() - t.width();
3016 int y = ypos_ + graphics::screen_height()/2;
3017
3018 graphics::blit_texture(t, x, y);
3019 t = font::render_text(next_level, graphics::color_black(), 24);
3020 x = lvl_->boundaries().x2();
3021 graphics::blit_texture(t, x, y);
3022 }
3023
3024 if(tool() == TOOL_ADD_OBJECT && !lvl_->editor_highlight()) {
3025 int x = round_tile_size(xpos_ + mousex*zoom_);
3026 int y = round_tile_size(ypos_ + mousey*zoom_);
3027 if(ctrl_pressed) {
3028 x = xpos_ + mousex*zoom_;
3029 y = ypos_ + mousey*zoom_;
3030 }
3031
3032 entity& e = *all_characters()[cur_object_].preview_object();
3033 e.set_pos(x, y);
3034 if(place_entity_in_level(*lvl_, e)) {
3035 glColor4f(1.0, 1.0, 1.0, 0.5);
3036 all_characters()[cur_object_].preview_frame()->draw(e.x(), e.y(), face_right_, upside_down_);
3037 glColor4f(1.0, 1.0, 1.0, 1.0);
3038 }
3039 }
3040 if(tool() == TOOL_EDIT_HEXES) {
3041 int x = (xpos_ + mousex*zoom_);
3042 int y = (ypos_ + mousey*zoom_);
3043 if(ctrl_pressed) {
3044 x = xpos_ + mousex*zoom_;
3045 y = ypos_ + mousey*zoom_;
3046 }
3047 point p = hex::hex_map::get_tile_pos_from_pixel_pos(x, y);
3048 glColor4f(1.0, 1.0, 1.0, 0.7);
3049 hex::hex_object::get_editor_tiles()[get_hex_tileset()]->get_editor_info().draw(p.x, p.y);
3050 glColor4f(1.0, 1.0, 1.0, 1.0);
3051 }
3052
3053 if(drawing_rect_) {
3054 int x1 = anchorx_;
3055 int x2 = xpos_ + mousex*zoom_;
3056 if(x1 > x2) {
3057 std::swap(x1,x2);
3058 }
3059
3060 int y1 = anchory_;
3061 int y2 = ypos_ + mousey*zoom_;
3062 if(y1 > y2) {
3063 std::swap(y1,y2);
3064 }
3065
3066 const SDL_Rect rect = {x1, y1, x2 - x1, y2 - y1};
3067 const SDL_Color color = {255,255,255,255};
3068 graphics::draw_hollow_rect(rect, color);
3069 }
3070
3071 std::vector<GLfloat>& varray = graphics::global_vertex_array();
3072 if(property_dialog_ && property_dialog_.get() == current_dialog_ &&
3073 property_dialog_->get_entity() &&
3074 property_dialog_->get_entity()->editor_info() &&
3075 std::count(lvl_->get_chars().begin(), lvl_->get_chars().end(),
3076 property_dialog_->get_entity())) {
3077 #if !defined(USE_GLES2)
3078 glDisable(GL_TEXTURE_2D);
3079 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
3080 #endif
3081
3082 //number of variables seen of each type, used to
3083 //cycle through colors for each variable type.
3084 std::map<editor_variable_info::VARIABLE_TYPE, int> nseen_variables;
3085
3086 int selected_index = -1;
3087 const editor_variable_info* selected_var = variable_info_selected(property_dialog_->get_entity(), xpos_ + mousex*zoom_, ypos_ + mousey*zoom_, zoom_, &selected_index);
3088 foreach(const editor_variable_info& var, property_dialog_->get_entity()->editor_info()->vars()) {
3089 const std::string& name = var.variable_name();
3090 const editor_variable_info::VARIABLE_TYPE type = var.type();
3091 const int color_index = nseen_variables[type]++;
3092 variant value = property_dialog_->get_entity()->query_value(name);
3093 graphics::color color;
3094 switch(color_index) {
3095 case 0: color = graphics::color(255, 0, 0, 255); break;
3096 case 1: color = graphics::color(0, 255, 0, 255); break;
3097 case 2: color = graphics::color(0, 0, 255, 255); break;
3098 case 3: color = graphics::color(255, 255, 0, 255); break;
3099 default:color = graphics::color(255, 0, 255, 255); break;
3100 }
3101
3102 if(&var == selected_var) {
3103 glColor4ub(255, 255, 0, 255);
3104 } else {
3105 glColor4ub(color.r(), color.g(), color.b(), color.a());
3106 }
3107
3108 varray.clear();
3109 switch(type) {
3110 case editor_variable_info::XPOSITION:
3111 if(value.is_int()) {
3112 varray.push_back(value.as_int()); varray.push_back(ypos_);
3113 varray.push_back(value.as_int()); varray.push_back(ypos_ + graphics::screen_height()*zoom_);
3114 }
3115 break;
3116 case editor_variable_info::YPOSITION:
3117 if(value.is_int()) {
3118 varray.push_back(xpos_); varray.push_back(value.as_int());
3119 varray.push_back(xpos_ + graphics::screen_width()*zoom_); varray.push_back(value.as_int());
3120 }
3121 break;
3122 case editor_variable_info::TYPE_POINTS:
3123 if(value.is_list()) {
3124 std::vector<variant> items = value.as_list();
3125
3126 int index = 0;
3127 foreach(const variant& item, items) {
3128 point p(item);
3129 graphics::color col = color;
3130 if(&var == selected_var && index == selected_index) {
3131 col = graphics::color(255, 255, 0, 255);
3132 }
3133
3134 graphics::draw_rect(rect(p.x, p.y-10, 1, 20), col);
3135 graphics::draw_rect(rect(p.x-10, p.y, 20, 1), col);
3136
3137 graphics::blit_texture(font::render_text(formatter() << (index+1), col.as_sdl_color(), 12), p.x+4, p.y-14);
3138 ++index;
3139 }
3140 }
3141 break;
3142 default:
3143 break;
3144 }
3145
3146 if(!varray.empty()) {
3147 #if defined(USE_GLES2)
3148 gles2::manager gles2_manager(gles2::get_simple_shader());
3149 gles2::active_shader()->shader()->vertex_array(2, GL_FLOAT, 0, 0, &varray.front());
3150 #else
3151 glVertexPointer(2, GL_FLOAT, 0, &varray.front());
3152 #endif
3153 glDrawArrays(GL_LINES, 0, varray.size()/2);
3154 }
3155 }
3156 #if !defined(USE_GLES2)
3157 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
3158 glEnable(GL_TEXTURE_2D);
3159 #endif
3160 }
3161
3162 if(g_draw_stats) {
3163 // stats::draw_stats(stats_);
3164 }
3165
3166 glPopMatrix();
3167
3168 //draw the difficulties of segments.
3169 if(lvl_->segment_width() > 0 || lvl_->segment_height() > 0) {
3170 const int seg_width = lvl_->segment_width() ? lvl_->segment_width() : lvl_->boundaries().w();
3171 const int seg_height = lvl_->segment_height() ? lvl_->segment_height() : lvl_->boundaries().h();
3172 rect boundaries = modify_selected_rect(*this, lvl_->boundaries(), selectx, selecty);
3173 int seg = 0;
3174 for(int ypos = boundaries.y(); ypos < boundaries.y2(); ypos += seg_height) {
3175 const int y1 = ypos/zoom_;
3176 for(int xpos = boundaries.x(); xpos < boundaries.x2(); xpos += seg_width) {
3177 const int difficulty = lvl_->get_var(formatter() << "segment_difficulty_start_" << seg).as_int();
3178 // if(difficulty) {
3179 graphics::blit_texture(font::render_text(formatter() << "Difficulty: " << difficulty, graphics::color_white(), 14), (xpos - xpos_)/zoom_, y1 - 20 - ypos_/zoom_);
3180 // }
3181
3182 ++seg;
3183 }
3184 }
3185 }
3186
3187 //draw grid
3188 if(g_draw_grid){
3189 #if !defined(USE_GLES2)
3190 glDisable(GL_TEXTURE_2D);
3191 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
3192 #endif
3193 varray.clear();
3194 glColor4ub(255, 255, 255, 64);
3195 for(int x = -TileSize - (xpos_/zoom_)%TileSize; x < graphics::screen_width(); x += 32/zoom_) {
3196 varray.push_back(x); varray.push_back(0);
3197 varray.push_back(x); varray.push_back(graphics::screen_height());
3198 }
3199
3200 for(int y = -TileSize - (ypos_/zoom_)%TileSize; y < graphics::screen_height(); y += 32/zoom_) {
3201 varray.push_back(0); varray.push_back(y);
3202 varray.push_back(graphics::screen_width()); varray.push_back(y);
3203 }
3204 }
3205
3206 #if defined(USE_GLES2)
3207 {
3208 gles2::manager gles2_manager(gles2::get_simple_shader());
3209 if(g_draw_grid) {
3210 gles2::active_shader()->shader()->vertex_array(2, GL_FLOAT, 0, 0, &varray.front());
3211 }
3212 #else
3213 if(g_draw_grid) {
3214 glVertexPointer(2, GL_FLOAT, 0, &varray.front());
3215 }
3216 #endif
3217
3218 if(g_draw_grid) {
3219 glDrawArrays(GL_LINES, 0, varray.size()/2);
3220 }
3221
3222 // draw level boundaries in clear white
3223 {
3224 varray.clear();
3225 std::vector<GLfloat>& carray = graphics::global_texcoords_array(); //reusing texcoords array for colors
3226 carray.clear();
3227 rect boundaries = modify_selected_rect(*this, lvl_->boundaries(), selectx, selecty);
3228 const int x1 = boundaries.x()/zoom_;
3229 const int x2 = boundaries.x2()/zoom_;
3230 const int y1 = boundaries.y()/zoom_;
3231 const int y2 = boundaries.y2()/zoom_;
3232
3233 graphics::color selected_color(255, 255, 0, 255);
3234 graphics::color normal_color(255, 255, 255, 255);
3235
3236 if(resizing_top_level_edge || rect_top_edge_selected(lvl_->boundaries(), selectx, selecty, zoom_)) {
3237 selected_color.add_to_vector(&carray);
3238 selected_color.add_to_vector(&carray);
3239 } else {
3240 normal_color.add_to_vector(&carray);
3241 normal_color.add_to_vector(&carray);
3242 }
3243
3244 varray.push_back(x1 - xpos_/zoom_); varray.push_back(y1 - ypos_/zoom_);
3245 varray.push_back(x2 - xpos_/zoom_); varray.push_back(y1 - ypos_/zoom_);
3246
3247 if(resizing_left_level_edge || rect_left_edge_selected(lvl_->boundaries(), selectx, selecty, zoom_)) {
3248 selected_color.add_to_vector(&carray);
3249 selected_color.add_to_vector(&carray);
3250 } else {
3251 normal_color.add_to_vector(&carray);
3252 normal_color.add_to_vector(&carray);
3253 }
3254
3255 varray.push_back(x1 - xpos_/zoom_); varray.push_back(y1 - ypos_/zoom_);
3256 varray.push_back(x1 - xpos_/zoom_); varray.push_back(y2 - ypos_/zoom_);
3257
3258 if(resizing_right_level_edge || rect_right_edge_selected(lvl_->boundaries(), selectx, selecty, zoom_)) {
3259 selected_color.add_to_vector(&carray);
3260 selected_color.add_to_vector(&carray);
3261 } else {
3262 normal_color.add_to_vector(&carray);
3263 normal_color.add_to_vector(&carray);
3264 }
3265
3266 varray.push_back(x2 - xpos_/zoom_); varray.push_back(y1 - ypos_/zoom_);
3267 varray.push_back(x2 - xpos_/zoom_); varray.push_back(y2 - ypos_/zoom_);
3268
3269 if(resizing_bottom_level_edge || rect_bottom_edge_selected(lvl_->boundaries(), selectx, selecty, zoom_)) {
3270 selected_color.add_to_vector(&carray);
3271 selected_color.add_to_vector(&carray);
3272 } else {
3273 normal_color.add_to_vector(&carray);
3274 normal_color.add_to_vector(&carray);
3275 }
3276
3277 varray.push_back(x1 - xpos_/zoom_); varray.push_back(y2 - ypos_/zoom_);
3278 varray.push_back(x2 - xpos_/zoom_); varray.push_back(y2 - ypos_/zoom_);
3279
3280 if(lvl_->segment_width() > 0) {
3281 for(int xpos = boundaries.x() + lvl_->segment_width(); xpos < boundaries.x2(); xpos += lvl_->segment_width()) {
3282 varray.push_back((xpos - xpos_)/zoom_);
3283 varray.push_back(y1 - ypos_/zoom_);
3284 varray.push_back((xpos - xpos_)/zoom_);
3285 varray.push_back(y2 - ypos_/zoom_);
3286 normal_color.add_to_vector(&carray);
3287 normal_color.add_to_vector(&carray);
3288 }
3289 }
3290
3291 if(lvl_->segment_height() > 0) {
3292 for(int ypos = boundaries.y() + lvl_->segment_height(); ypos < boundaries.y2(); ypos += lvl_->segment_height()) {
3293 varray.push_back(x1 - xpos_/zoom_);
3294 varray.push_back((ypos - ypos_)/zoom_);
3295 varray.push_back(x2 - xpos_/zoom_);
3296 varray.push_back((ypos - ypos_)/zoom_);
3297 normal_color.add_to_vector(&carray);
3298 normal_color.add_to_vector(&carray);
3299 }
3300 }
3301
3302 #if defined(USE_GLES2)
3303 {
3304 gles2::manager gles2_manager(gles2::get_simple_col_shader());
3305 gles2::active_shader()->shader()->vertex_array(2, GL_FLOAT, 0, 0, &varray.front());
3306 gles2::active_shader()->shader()->color_array(4, GL_FLOAT, 0, 0, &carray.front());
3307 glDrawArrays(GL_LINES, 0, varray.size()/2);
3308 }
3309 #else
3310 glEnableClientState(GL_COLOR_ARRAY);
3311 glVertexPointer(2, GL_FLOAT, 0, &varray.front());
3312 glColorPointer(4, GL_FLOAT, 0, &carray.front());
3313 glDrawArrays(GL_LINES, 0, varray.size()/2);
3314 glDisableClientState(GL_COLOR_ARRAY);
3315 #endif
3316 }
3317
3318 draw_selection(0, 0);
3319
3320 if(dragging_) {
3321 int diffx = (selectx - anchorx_)/TileSize;
3322 int diffy = (selecty - anchory_)/TileSize;
3323
3324 if(diffx != 0 || diffy != 0) {
3325 std::cerr << "DRAW DIFF: " << diffx << "," << diffy << "\n";
3326 draw_selection(diffx*TileSize, diffy*TileSize);
3327 }
3328 }
3329
3330 if(tool() == TOOL_EDIT_SEGMENTS && selected_segment_ >= 0) {
3331 rect area = rect(lvl_->boundaries().x() + selected_segment_*lvl_->segment_width(), lvl_->boundaries().y() + selected_segment_*lvl_->segment_height(),
3332 lvl_->segment_width() ? lvl_->segment_width() : lvl_->boundaries().w(),
3333 lvl_->segment_height() ? lvl_->segment_height() : lvl_->boundaries().h());
3334 area = rect((area.x() - xpos_)/zoom_, (area.y() - ypos_)/zoom_,
3335 area.w()/zoom_, area.h()/zoom_);
3336 graphics::draw_rect(area, graphics::color(255, 255, 0, 64));
3337
3338 variant next = lvl_->get_var(formatter() << "segments_after_" << selected_segment_);
3339 if(next.is_list()) {
3340 for(int n = 0; n != next.num_elements(); ++n) {
3341 const int segment = next[n].as_int();
3342 rect area = rect(lvl_->boundaries().x() + segment*lvl_->segment_width(), lvl_->boundaries().y() + segment*lvl_->segment_height(),
3343 lvl_->segment_width() ? lvl_->segment_width() : lvl_->boundaries().w(),
3344 lvl_->segment_height() ? lvl_->segment_height() : lvl_->boundaries().h());
3345 area = rect((area.x() - xpos_)/zoom_, (area.y() - ypos_)/zoom_,
3346 area.w()/zoom_, area.h()/zoom_);
3347 graphics::draw_rect(area, graphics::color(255, 0, 0, 64));
3348 }
3349 }
3350 }
3351
3352 #if !defined(USE_GLES2)
3353 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
3354 glEnable(GL_TEXTURE_2D);
3355 #else
3356 }
3357 #endif
3358
3359 glColor4f(1.0, 1.0, 1.0, 1.0);
3360 graphics::texture xtex = font::render_text(formatter() << (xpos_ + mousex*zoom_) << ",", graphics::color_white(), 14);
3361 graphics::texture ytex = font::render_text(formatter() << (ypos_ + mousey*zoom_), graphics::color_white(), 14);
3362
3363 graphics::blit_texture(xtex, 10, 80);
3364 graphics::blit_texture(ytex, 10 + xtex.width(), 80);
3365
3366 if(tool() == TOOL_EDIT_HEXES) {
3367 point p = hex::hex_map::get_tile_pos_from_pixel_pos(xpos_ + mousex*zoom_, ypos_ + mousey*zoom_);
3368 graphics::texture xptex = font::render_text(formatter() << "(" << p.x << ",", graphics::color_white(), 14);
3369 graphics::texture yptex = font::render_text(formatter() << p.y << ")", graphics::color_white(), 14);
3370 graphics::blit_texture(xptex, 90, 80);
3371 graphics::blit_texture(yptex, 90 + xptex.width(), 80);
3372 // XXX: generate the name / editor name of the tile under the mouse and display it.
3373 }
3374
3375 if(!code_dialog_ && current_dialog_) {
3376 current_dialog_->draw();
3377 }
3378
3379 if(!code_dialog_ && layers_dialog_) {
3380 layers_dialog_->draw();
3381 }
3382
3383 editor_menu_dialog_->draw();
3384
3385 if(!code_dialog_) {
3386 editor_mode_dialog_->draw();
3387 }
3388
3389 if(code_dialog_) {
3390 code_dialog_->draw();
3391 }
3392
3393 gui::draw_tooltip();
3394 }
3395
draw_selection(int xoffset,int yoffset) const3396 void editor::draw_selection(int xoffset, int yoffset) const
3397 {
3398 if(tile_selection_.empty()) {
3399 return;
3400 }
3401
3402 const int ticks = (SDL_GetTicks()/40)%16;
3403 uint32_t stipple_bits = 0xFF;
3404 stipple_bits <<= ticks;
3405 const uint16_t stipple_mask = (stipple_bits&0xFFFF) | ((stipple_bits&0xFFFF0000) >> 16);
3406
3407 glColor4ub(255, 255, 255, 255);
3408 #if !defined(SDL_VIDEO_OPENGL_ES) && (!defined(USE_GLES2) || !defined(GL_ES_VERSION_2_0))
3409 glEnable(GL_LINE_STIPPLE);
3410 glLineStipple(1, stipple_mask);
3411 #endif
3412 std::vector<GLfloat>& varray = graphics::global_vertex_array();
3413 varray.clear();
3414 foreach(const point& p, tile_selection_.tiles) {
3415 const int size = TileSize/zoom_;
3416 const int xpos = xoffset/zoom_ + p.x*size - xpos_/zoom_;
3417 const int ypos = yoffset/zoom_ + p.y*size - ypos_/zoom_;
3418
3419 if(std::binary_search(tile_selection_.tiles.begin(), tile_selection_.tiles.end(), point(p.x, p.y - 1)) == false) {
3420 varray.push_back(xpos); varray.push_back(ypos);
3421 varray.push_back(xpos + size); varray.push_back(ypos);
3422 }
3423
3424 if(std::binary_search(tile_selection_.tiles.begin(), tile_selection_.tiles.end(), point(p.x, p.y + 1)) == false) {
3425 varray.push_back(xpos + size); varray.push_back(ypos + size);
3426 varray.push_back(xpos); varray.push_back(ypos + size);
3427 }
3428
3429 if(std::binary_search(tile_selection_.tiles.begin(), tile_selection_.tiles.end(), point(p.x - 1, p.y)) == false) {
3430 varray.push_back(xpos); varray.push_back(ypos + size);
3431 varray.push_back(xpos); varray.push_back(ypos);
3432 }
3433
3434 if(std::binary_search(tile_selection_.tiles.begin(), tile_selection_.tiles.end(), point(p.x + 1, p.y)) == false) {
3435 varray.push_back(xpos + size); varray.push_back(ypos);
3436 varray.push_back(xpos + size); varray.push_back(ypos + size);
3437 }
3438 }
3439 #if defined(USE_GLES2)
3440 gles2::manager gles2_manager(gles2::get_simple_shader());
3441 gles2::active_shader()->shader()->vertex_array(2, GL_FLOAT, 0, 0, &varray.front());
3442 #else
3443 glVertexPointer(2, GL_FLOAT, 0, &varray.front());
3444 #endif
3445 glDrawArrays(GL_LINES, 0, varray.size()/2);
3446 #if !defined(SDL_VIDEO_OPENGL_ES) && (!defined(USE_GLES2) || !defined(GL_ES_VERSION_2_0))
3447 glDisable(GL_LINE_STIPPLE);
3448 glLineStipple(1, 0xFFFF);
3449 #endif
3450 }
3451
run_script(const std::string & id)3452 void editor::run_script(const std::string& id)
3453 {
3454 editor_script::execute(id, *this);
3455 }
3456
execute_command(boost::function<void ()> command,boost::function<void ()> undo,EXECUTABLE_COMMAND_TYPE type)3457 void editor::execute_command(boost::function<void()> command, boost::function<void()> undo, EXECUTABLE_COMMAND_TYPE type)
3458 {
3459 level_changed_++;
3460
3461 command();
3462
3463 executable_command cmd;
3464 cmd.redo_command = command;
3465 cmd.undo_command = undo;
3466 cmd.type = type;
3467 undo_.push_back(cmd);
3468 redo_.clear();
3469 }
3470
begin_command_group()3471 void editor::begin_command_group()
3472 {
3473 undo_commands_groups_.push(undo_.size());
3474
3475 lvl_->editor_freeze_tile_updates(true);
3476 }
3477
end_command_group()3478 void editor::end_command_group()
3479 {
3480 lvl_->editor_freeze_tile_updates(false);
3481
3482 ASSERT_NE(undo_commands_groups_.empty(), true);
3483
3484 const int index = undo_commands_groups_.top();
3485 undo_commands_groups_.pop();
3486
3487 if(index >= undo_.size()) {
3488 return;
3489 }
3490
3491 //group all of the commands since beginning into one command
3492 std::vector<boost::function<void()> > undo, redo;
3493 for(int n = index; n != undo_.size(); ++n) {
3494 undo.push_back(undo_[n].undo_command);
3495 redo.push_back(undo_[n].redo_command);
3496 }
3497
3498 //reverse the undos, since we want them executed in reverse order.
3499 std::reverse(undo.begin(), undo.end());
3500
3501 //make it so undoing and redoing will freeze tile updates during the
3502 //group command, and then do a full refresh of tiles once we're done.
3503 undo.insert(undo.begin(), boost::bind(&level::editor_freeze_tile_updates, lvl_.get(), true));
3504 undo.push_back(boost::bind(&level::editor_freeze_tile_updates, lvl_.get(), false));
3505 redo.insert(redo.begin(), boost::bind(&level::editor_freeze_tile_updates, lvl_.get(), true));
3506 redo.push_back(boost::bind(&level::editor_freeze_tile_updates, lvl_.get(), false));
3507
3508 executable_command cmd;
3509 cmd.redo_command = boost::bind(execute_functions, redo);
3510 cmd.undo_command = boost::bind(execute_functions, undo);
3511
3512 //replace all the individual commands with the one group command.
3513 undo_.erase(undo_.begin() + index, undo_.end());
3514 undo_.push_back(cmd);
3515 }
3516
undo_command()3517 void editor::undo_command()
3518 {
3519 if(undo_.empty()) {
3520 return;
3521 }
3522
3523 --level_changed_;
3524
3525 undo_.back().undo_command();
3526 redo_.push_back(undo_.back());
3527 undo_.pop_back();
3528
3529 if(layers_dialog_) {
3530 layers_dialog_->init();
3531 }
3532 }
3533
redo_command()3534 void editor::redo_command()
3535 {
3536 if(redo_.empty()) {
3537 return;
3538 }
3539
3540 ++level_changed_;
3541
3542 redo_.back().redo_command();
3543 undo_.push_back(redo_.back());
3544 redo_.pop_back();
3545
3546 if(layers_dialog_) {
3547 layers_dialog_->init();
3548 }
3549 }
3550
3551 void show_object_editor_dialog(const std::string& obj_type);
3552
3553 void launch_object_editor(const std::vector<std::string>& args);
3554
edit_level_properties()3555 void editor::edit_level_properties()
3556 {
3557 editor_dialogs::editor_level_properties_dialog prop_dialog(*this);
3558 prop_dialog.show_modal();
3559 }
3560
create_new_module()3561 void editor::create_new_module()
3562 {
3563 editor_dialogs::editor_module_properties_dialog prop_dialog(*this);
3564 prop_dialog.show_modal();
3565 if(prop_dialog.cancelled() == false) {
3566 prop_dialog.on_exit();
3567 close();
3568 g_last_edited_level() = prop_dialog.on_exit();
3569 SDL_WM_SetCaption(module::get_module_pretty_name().c_str(), module::get_module_pretty_name().c_str());
3570 }
3571 }
3572
edit_module_properties()3573 void editor::edit_module_properties()
3574 {
3575 editor_dialogs::editor_module_properties_dialog prop_dialog(*this, module::get_module_name());
3576 prop_dialog.show_modal();
3577 if(prop_dialog.cancelled() == false) {
3578 prop_dialog.on_exit();
3579 SDL_WM_SetCaption(module::get_module_pretty_name().c_str(), module::get_module_pretty_name().c_str());
3580 }
3581 }
3582
3583 namespace {
do_draw_scene()3584 void do_draw_scene() {
3585 draw_scene(level::current(), last_draw_position());
3586 }
3587 }
3588
create_new_object()3589 void editor::create_new_object()
3590 {
3591 editor_dialogs::custom_object_dialog object_dialog(*this,
3592 preferences::virtual_screen_width()*0.05,
3593 preferences::virtual_screen_height()*0.05,
3594 preferences::virtual_screen_width()*0.9,
3595 preferences::virtual_screen_height()*0.9);
3596 object_dialog.set_background_frame("empty_window");
3597 object_dialog.set_draw_background_fn(gui::dialog::draw_last_scene);
3598 object_dialog.show_modal();
3599 if(object_dialog.cancelled() == false) {
3600 customobjecttype::reload_file_paths();
3601 lvl_->editor_clear_selection();
3602 change_tool(TOOL_ADD_OBJECT);
3603 const std::string type = object_dialog.get_object()["id"].as_string();
3604 const_custom_object_type_ptr obj = custom_object_type::get(type);
3605
3606 if(obj->editor_info()) {
3607 all_characters().push_back(editor::enemy_type(type, obj->editor_info()->category(), variant()));
3608 current_dialog_ = character_dialog_.get();
3609
3610 for(int n = 0; n != all_characters().size(); ++n) {
3611 const enemy_type& c = all_characters()[n];
3612 if(c.node["type"].as_string() == type) {
3613 character_dialog_->select_category(c.category);
3614 character_dialog_->set_character(n);
3615 }
3616 }
3617 }
3618 }
3619 }
3620
edit_shaders()3621 void editor::edit_shaders()
3622 {
3623 #if defined(USE_GLES2)
3624 const std::string path = module::map_file("data/shaders.cfg");
3625 if(sys::file_exists(path) == false) {
3626 sys::write_file(path, "{\n\t\"shaders\": {\n\t},\n\t\"programs\": [\n\t],\n}");
3627 }
3628 if(external_code_editor_ && external_code_editor_->replace_in_game_editor()) {
3629
3630 std::cerr << "Loading file in external editor: " << path << "\n";
3631 external_code_editor_->load_file(path);
3632 }
3633
3634 if(code_dialog_) {
3635 code_dialog_.reset();
3636 } else {
3637 code_dialog_.reset(new code_editor_dialog(rect(graphics::screen_width() - 620, 30, 620, graphics::screen_height() - 30)));
3638 code_dialog_->load_file(path);
3639 }
3640 #endif
3641 }
3642
3643
add_multi_object_to_level(level_ptr lvl,entity_ptr e)3644 void editor::add_multi_object_to_level(level_ptr lvl, entity_ptr e)
3645 {
3646 current_level_scope scope(lvl.get());
3647 lvl->add_multi_player(e);
3648 e->handle_event("editor_added");
3649 }
3650
add_object_to_level(level_ptr lvl,entity_ptr e)3651 void editor::add_object_to_level(level_ptr lvl, entity_ptr e)
3652 {
3653 current_level_scope scope(lvl.get());
3654 lvl->add_character(e);
3655 e->handle_event("editor_added");
3656 }
3657
remove_object_from_level(level_ptr lvl,entity_ptr e)3658 void editor::remove_object_from_level(level_ptr lvl, entity_ptr e)
3659 {
3660 current_level_scope scope(lvl.get());
3661 e->handle_event("editor_removed");
3662 lvl->remove_character(e);
3663 lvl->set_active_chars();
3664 }
3665
mutate_object_value(level_ptr lvl,entity_ptr e,const std::string & value,variant new_value)3666 void editor::mutate_object_value(level_ptr lvl, entity_ptr e, const std::string& value, variant new_value)
3667 {
3668 current_level_scope scope(lvl.get());
3669 e->handle_event("editor_changing_variable");
3670 e->mutate_value(value, new_value);
3671 e->handle_event("editor_changed_variable");
3672 }
3673
generate_mutate_commands(entity_ptr c,const std::string & attr,variant new_value,std::vector<boost::function<void ()>> & undo,std::vector<boost::function<void ()>> & redo)3674 void editor::generate_mutate_commands(entity_ptr c, const std::string& attr, variant new_value, std::vector<boost::function<void()> >& undo, std::vector<boost::function<void()> >& redo)
3675 {
3676 if(!c || c->spawned_by().empty() == false) {
3677 return;
3678 }
3679
3680 foreach(level_ptr lvl, levels_) {
3681 entity_ptr obj = lvl->get_entity_by_label(c->label());
3682 if(!obj) {
3683 continue;
3684 }
3685 const game_logic::formula_callable* obj_vars = obj->query_value("vars").as_callable();
3686 variant current_value = obj_vars->query_value(attr);
3687
3688 redo.push_back(boost::bind(&editor::mutate_object_value, this, lvl, obj, attr, new_value));
3689 undo.push_back(boost::bind(&editor::mutate_object_value, this, lvl, obj, attr, current_value));
3690 }
3691 }
3692
generate_remove_commands(entity_ptr c,std::vector<boost::function<void ()>> & undo,std::vector<boost::function<void ()>> & redo)3693 void editor::generate_remove_commands(entity_ptr c, std::vector<boost::function<void()> >& undo, std::vector<boost::function<void()> >& redo)
3694 {
3695 if(!c || c->spawned_by().empty() == false) {
3696 return;
3697 }
3698
3699 foreach(level_ptr lvl, levels_) {
3700 entity_ptr obj = lvl->get_entity_by_label(c->label());
3701 if(!obj) {
3702 continue;
3703 }
3704
3705 redo.push_back(boost::bind(&editor::remove_object_from_level, this, lvl, obj));
3706 undo.push_back(boost::bind(&editor::add_object_to_level, this, lvl, obj));
3707 if(obj->label().empty() == false) {
3708 foreach(entity_ptr child, lvl->get_chars()) {
3709 if(child->spawned_by() == obj->label()) {
3710 std::cerr << "REMOVING CHILD OBJECT: " << child->debug_description() << " " << child->label() << "\n";
3711 redo.push_back(boost::bind(&editor::remove_object_from_level, this, lvl, child));
3712 undo.push_back(boost::bind(&editor::add_object_to_level, this, lvl, child));
3713 }
3714 }
3715 }
3716 }
3717 }
3718
has_keyboard_focus() const3719 bool editor::has_keyboard_focus() const
3720 {
3721 if(code_dialog_ && code_dialog_->has_keyboard_focus()) {
3722 return true;
3723 }
3724
3725 if(current_dialog_ && current_dialog_->has_focus()) {
3726 return true;
3727 }
3728
3729 return false;
3730 }
3731
toggle_code()3732 void editor::toggle_code()
3733 {
3734 if(external_code_editor_ && external_code_editor_->replace_in_game_editor()) {
3735
3736 std::string type;
3737 if(lvl_->editor_selection().empty() == false) {
3738 type = lvl_->editor_selection().back()->query_value("type").as_string();
3739 }
3740
3741 if(type.empty()) {
3742 std::cerr << "no object selected to open code for\n";
3743 } else {
3744 //if this is a nested type, convert it to their parent type.
3745 std::string::iterator dot_itor = std::find(type.begin(), type.end(), '.');
3746 type.erase(dot_itor, type.end());
3747
3748 const std::string* path = custom_object_type::get_object_path(type + ".cfg");
3749 ASSERT_LOG(path, "Could not find path for object " << type);
3750 std::cerr << "Loading file in external editor: " << *path << "\n";
3751 external_code_editor_->load_file(*path);
3752 }
3753
3754 return;
3755 }
3756
3757 if(code_dialog_) {
3758 code_dialog_.reset();
3759 } else {
3760 code_dialog_.reset(new code_editor_dialog(rect(graphics::screen_width() - 620, 30, 620, graphics::screen_height() - 30)));
3761 set_code_file();
3762 }
3763 }
3764
set_code_file()3765 void editor::set_code_file()
3766 {
3767 if(tool_ == TOOL_ADD_RECT || tool_ == TOOL_SELECT_RECT || tool_ == TOOL_MAGIC_WAND || tool_ == TOOL_PENCIL) {
3768 std::cerr << "SET TILESET..\n";
3769 if(cur_tileset_ >= 0 && cur_tileset_ < tilesets.size()) {
3770 const std::vector<std::string>& files = tile_map::get_files(tilesets[cur_tileset_].type);
3771 std::cerr << "TILESET: " << files.size() << " FOR " << tilesets[cur_tileset_].type << "\n";
3772 foreach(const std::string& file, files) {
3773 std::map<std::string, std::string> fnames;
3774 module::get_unique_filenames_under_dir("data/tiles", &fnames);
3775 const std::map<std::string, std::string>::const_iterator itor =
3776 module::find(fnames, file);
3777 if(itor != fnames.end() && code_dialog_) {
3778 std::cerr << "TILESET FNAME: " << itor->second << "\n";
3779 code_dialog_->load_file(itor->second);
3780 }
3781 }
3782 }
3783
3784 return;
3785 }
3786
3787 std::string type;
3788 if(lvl_->editor_selection().empty() == false) {
3789 type = lvl_->editor_selection().back()->query_value("type").as_string();
3790 } else if(lvl_->player()) {
3791 type = lvl_->player()->get_entity().query_value("type").as_string();
3792 }
3793
3794 if(type.empty()) {
3795 return;
3796 }
3797
3798 if(std::count(type.begin(), type.end(), '.')) {
3799 //it's a subtype, so find the parent.
3800 type = std::string(type.begin(), std::find(type.begin(), type.end(), '.'));
3801 }
3802
3803 const std::string* path = custom_object_type::get_object_path(type + ".cfg");
3804 if(path && code_dialog_) {
3805 code_dialog_->load_file(*path);
3806 }
3807 }
3808
start_adding_points(const std::string & field_name)3809 void editor::start_adding_points(const std::string& field_name)
3810 {
3811 adding_points_ = field_name;
3812
3813 if(property_dialog_) {
3814 property_dialog_->init();
3815 }
3816 }
3817 #endif // !NO_EDITOR
3818
3819