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