some cleanups memory leak fixes and moving of source files
[supertux.git] / src / leveleditor.cpp
1 /***************************************************************************
2                   leveleditor.cpp  -  built'in leveleditor
3                      -------------------
4     begin                : June, 23 2004
5     copyright            : (C) 2004 by Ricardo Cruz
6     email                : rick2@aeiou.pt
7  ***************************************************************************/
8
9 /***************************************************************************
10  *                                                                         *
11  *   This program is free software; you can redistribute it and/or modify  *
12  *   it under the terms of the GNU General Public License as published by  *
13  *   the Free Software Foundation; either version 2 of the License, or     *
14  *   (at your option) any later version.                                   *
15  *                                                                         *
16  ***************************************************************************/
17
18 #include <config.h>
19
20 #include <stdlib.h>
21 #include <algorithm>
22
23 #include "gui/mousecursor.h"
24 #include "gui/menu.h"
25 #include "gui/button.h"
26 #include "audio/sound_manager.h"
27 #include "app/gettext.h"
28 #include "app/setup.h"
29 #include "app/globals.h"
30 #include "special/sprite.h"
31 #include "leveleditor.h"
32 #include "resources.h"
33 #include "tile.h"
34 #include "tile_manager.h"
35 #include "sector.h"
36 #include "gameloop.h"
37 #include "object/gameobjs.h"
38 #include "object/camera.h"
39 #include "object/tilemap.h"
40 #include "object/background.h"
41
42 LevelEditor::LevelEditor()
43 {
44   show_grid = true;
45
46   selection.clear();
47   selection_end = selection_ini = Vector(0,0);
48   left_button = middle_button = mouse_moved =  false;
49   level = 0;
50   level_subset = 0;
51
52   cur_layer = LAYER_TILES;
53   level_changed = false;
54
55   sector = 0;
56   zoom = 1.0;
57
58   /* Creating menus */
59   level_subsets = FileSystem::dsubdirs("/levels", "info");
60   subset_menu = new Menu();
61   subset_menu->additem(MN_LABEL,_("Load Subset"),0,0);
62   subset_menu->additem(MN_HL,"",0,0);
63   int i = 0;
64   for(std::set<std::string>::iterator it = level_subsets.begin(); it != level_subsets.end(); ++it, ++i)
65     subset_menu->additem(MN_ACTION, (*it),0,0,i);
66   subset_menu->additem(MN_HL,"",0,0);
67   subset_menu->additem(MN_BACK,_("Back"),0,0);
68
69   create_subset_menu = new Menu();
70   create_subset_menu->additem(MN_LABEL,_("New Level Subset"),0,0);
71   create_subset_menu->additem(MN_HL,"",0,0);
72   create_subset_menu->additem(MN_TEXTFIELD,_("Filename   "),0,0,MN_ID_FILENAME_SUBSET);
73   create_subset_menu->additem(MN_TEXTFIELD,_("Title      "),0,0,MN_ID_TITLE_SUBSET);
74   create_subset_menu->additem(MN_TEXTFIELD,_("Description"),0,0,MN_ID_DESCRIPTION_SUBSET);
75   create_subset_menu->additem(MN_ACTION,_("Create"),0,0, MN_ID_CREATE_SUBSET);
76   create_subset_menu->additem(MN_HL,"",0,0);
77   create_subset_menu->additem(MN_BACK,_("Back"),0,0);
78
79   main_menu = new Menu();
80   main_menu->additem(MN_LABEL,_("Level Editor Menu"),0,0);
81   main_menu->additem(MN_HL,"",0,0);
82   main_menu->additem(MN_ACTION,_("Return to Level Editor"),0,0,MN_ID_RETURN);
83   main_menu->additem(MN_GOTO,_("Create Level Subset"),0,create_subset_menu);
84   main_menu->additem(MN_GOTO,_("Load Level Subset"),0,subset_menu);
85   main_menu->additem(MN_HL,"",0,0);
86   main_menu->additem(MN_ACTION,_("Quit Level Editor"),0,0,MN_ID_QUIT);
87
88   settings_menu = new Menu();
89   settings_menu->additem(MN_LABEL,_("Level Settings"),0,0);
90   settings_menu->additem(MN_HL,"",0,0);
91   settings_menu->additem(MN_TEXTFIELD,_("Name    "),0,0,MN_ID_NAME);
92   settings_menu->additem(MN_TEXTFIELD,_("Author  "),0,0,MN_ID_AUTHOR);
93   settings_menu->additem(MN_NUMFIELD, _("Width   "),0,0,MN_ID_WIDTH);
94   settings_menu->additem(MN_NUMFIELD, _("Height  "),0,0,MN_ID_HEIGHT);
95   settings_menu->additem(MN_HL,"",0,0);
96   settings_menu->additem(MN_ACTION,_("Apply"),0,0,MN_ID_APPLY_SETTINGS);
97
98   /* Creating button groups */
99   load_buttons_gfx();
100
101   tiles_board = new ButtonGroup(Vector(screen->w - 140, 100),
102             Vector(32,32), Vector(4,8));
103
104   TileManager* tilemanager = TileManager::instance();
105
106   tiles_board->add_button(Button(img_rubber_bt, _("Eraser"), SDLKey(SDLK_DELETE)), 0);
107   for(unsigned int id = 1; id < tilemanager->get_max_tileid(); id++)
108     {
109     const Tile* tile = tilemanager->get(id);
110     if(!tile)
111       continue;
112
113     Surface* surface;
114     if(tile->editor_images.size())
115       surface = tile->editor_images[0];
116     else if(tile->images.size())
117       surface = tile->images[0];
118     else
119       continue;
120
121     Button button = Button(surface, "", SDLKey(0));
122     tiles_board->add_button(button, id);
123     }
124
125   #if 0
126   for(int i = 0; i < NUM_BadGuyKinds; i++)
127     {
128     // filter bomb, since it is only for internal use, not for levels
129     if(i == BAD_BOMB)
130       continue;
131
132     BadGuyKind kind = BadGuyKind(i);
133     BadGuy badguy(kind, 0,0);
134     badguy.activate(LEFT);
135
136     Surface *img = badguy.get_image();
137     tiles_board->add_button(Button(img, "", SDLKey(SDLK_1+i)), -(i+1));
138     }
139   #endif
140
141   #if 0
142   tiles_board->add_button(Button(img_trampoline[0].get_frame(0), _("Trampoline"), SDLKey(0)), OBJ_TRAMPOLINE);
143   tiles_board->add_button(Button(img_flying_platform->get_frame(0), _("Flying Platform"), SDLKey(0)), OBJ_FLYING_PLATFORM);
144   #endif
145
146   tiles_layer = new ButtonGroup(Vector(12, screen->h-64), Vector(80,20), Vector(1,3));
147   tiles_layer->add_button(Button(img_foreground_bt, _("Edtit foreground tiles"),
148                          SDLK_F10), LAYER_FOREGROUNDTILES);
149   tiles_layer->add_button(Button(img_interactive_bt, _("Edit interactive tiles"),
150                          SDLK_F11), LAYER_TILES, true);
151   tiles_layer->add_button(Button(img_background_bt, _("Edit background tiles"),
152                          SDLK_F12), LAYER_BACKGROUNDTILES);
153
154   level_options = new ButtonGroup(Vector(screen->w-164, screen->h-36), Vector(32,32), Vector(5,1));
155   level_options->add_pair_of_buttons(Button(img_next_sector_bt, _("Next sector"), SDLKey(0)), BT_NEXT_SECTOR,
156                  Button(img_previous_sector_bt, _("Prevous sector"), SDLKey(0)), BT_PREVIOUS_SECTOR);
157   level_options->add_pair_of_buttons(Button(img_next_level_bt, _("Next level"), SDLKey(0)), BT_NEXT_LEVEL,
158                  Button(img_previous_level_bt, _("Prevous level"), SDLKey(0)), BT_PREVIOUS_LEVEL);
159   level_options->add_button(Button(img_save_level_bt, _("Save level"), SDLK_F5), BT_LEVEL_SAVE);
160   level_options->add_button(Button(img_test_level_bt, _("Test level"), SDLK_F6), BT_LEVEL_TEST);
161   level_options->add_button(Button(img_setup_level_bt, _("Setup level"), SDLK_F7), BT_LEVEL_SETUP);
162 }
163
164 LevelEditor::~LevelEditor()
165 {
166 free_buttons_gfx();
167
168 delete tiles_board;
169 delete tiles_layer;
170 delete level_options;
171
172 delete subset_menu;
173 delete create_subset_menu;
174 delete main_menu;
175 delete settings_menu;
176
177 delete level;
178 delete level_subset;
179 }
180
181 void LevelEditor::load_buttons_gfx()
182 {
183 img_foreground_bt = new Surface(datadir + "/images/leveleditor/foreground.png", true);
184 img_interactive_bt = new Surface(datadir + "/images/leveleditor/interactive.png", true);
185 img_background_bt = new Surface(datadir + "/images/leveleditor/background.png", true);
186
187 img_save_level_bt = new Surface(datadir + "/images/leveleditor/save-level.png", true);
188 img_test_level_bt = new Surface(datadir + "/images/leveleditor/test-level.png", true);
189 img_setup_level_bt = new Surface(datadir + "/images/leveleditor/setup-level.png", true);
190
191 img_rubber_bt = new Surface(datadir + "/images/leveleditor/rubber.png", true);
192
193 img_previous_level_bt = new Surface(datadir + "/images/leveleditor/previous-level.png", true);
194 img_next_level_bt = new Surface(datadir + "/images/leveleditor/next-level.png", true);
195 img_previous_sector_bt = new Surface(datadir + "/images/leveleditor/previous-sector.png", true);
196 img_next_sector_bt = new Surface(datadir + "/images/leveleditor/next-sector.png", true);
197 }
198
199 void LevelEditor::free_buttons_gfx()
200 {
201 delete img_foreground_bt;
202 delete img_interactive_bt;
203 delete img_background_bt;
204
205 delete img_save_level_bt;
206 delete img_test_level_bt;
207 delete img_setup_level_bt;
208
209 delete img_rubber_bt;
210
211 delete img_previous_level_bt;
212 delete img_next_level_bt;
213 delete img_previous_sector_bt;
214 delete img_next_sector_bt;
215 }
216
217 void LevelEditor::run(const std::string filename)
218 {
219 SoundManager::get()->halt_music();
220 Menu::set_current(0);
221
222 DrawingContext context;
223
224 if(!filename.empty())
225   {
226   level_nb = -1;
227   load_level(filename);
228   }
229 else
230   Menu::set_current(main_menu);
231
232 mouse_cursor->set_state(MC_NORMAL);
233
234 done = false;
235 while(!done)
236   {
237   events();
238   action();
239   draw(context);
240   }
241
242 if(level_changed)
243   if(confirm_dialog(NULL, _("Level not saved. Wanna to?")))
244     save_level();
245 }
246
247 void LevelEditor::events()
248 {
249 mouse_moved = false;
250
251 while(SDL_PollEvent(&event))
252   {
253   Menu* menu = Menu::current();
254   if(menu)
255     {
256     menu->event(event);
257     menu->action();
258     if(menu == main_menu)
259       {
260       switch (main_menu->check())
261         {
262         case MN_ID_RETURN:
263           Menu::set_current(0);
264           break;
265         case MN_ID_QUIT:
266           done = true;
267           break;
268         }
269       }
270     else if(menu == create_subset_menu)
271       {
272       // activate or deactivate Create button if any filename as been specified
273       if(create_subset_menu->get_item_by_id(MN_ID_FILENAME_SUBSET).input[0] == '\0')
274         create_subset_menu->get_item_by_id(MN_ID_CREATE_SUBSET).kind = MN_DEACTIVE;
275       else
276         create_subset_menu->get_item_by_id(MN_ID_CREATE_SUBSET).kind = MN_ACTION;
277
278       if(create_subset_menu->check() == MN_ID_CREATE_SUBSET)
279         {   // applying settings:
280         std::string subset_name = create_subset_menu->get_item_by_id(MN_ID_FILENAME_SUBSET).input;
281         LevelSubset::create(subset_name);
282
283         delete level_subset;
284         level_subset = new LevelSubset();
285         level_subset->load(create_subset_menu->get_item_by_id(MN_ID_FILENAME_SUBSET).input);
286
287         level_subset->title = create_subset_menu->get_item_by_id(MN_ID_TITLE_SUBSET).input;
288         level_subset->description = create_subset_menu->get_item_by_id(MN_ID_DESCRIPTION_SUBSET).input;
289         //FIXME: generate better level filenames
290         level_subset->add_level(subset_name+'/'+"new_level.stl");
291         Level* newlevel = new Level();
292         newlevel->add_sector(create_sector("main", 25, 19));
293         newlevel->save(level_subset->get_level_filename(0));
294         level_subset->save();
295         
296         load_level(0);
297
298         create_subset_menu->get_item_by_id(MN_ID_FILENAME_SUBSET).change_input("");
299         create_subset_menu->get_item_by_id(MN_ID_TITLE_SUBSET).change_input("");
300         create_subset_menu->get_item_by_id(MN_ID_DESCRIPTION_SUBSET).change_input("");
301         }
302       }
303     else if(menu == subset_menu)
304       {
305       int i = subset_menu->check();
306       if(i >= 0)
307         {
308         std::set<std::string>::iterator it = level_subsets.begin();
309         for(int t = 0; t < i; t++)
310           it++;
311         load_level_subset(*it);
312         Menu::set_current(0);
313         }
314       }
315     else if(menu == settings_menu)
316       {
317       if(settings_menu->check() == MN_ID_APPLY_SETTINGS)
318         {   // applying settings:
319         level_changed = true;
320
321         level->name = settings_menu->get_item_by_id(MN_ID_NAME).input;
322         level->author = settings_menu->get_item_by_id(MN_ID_AUTHOR).input;
323
324         solids->resize(atoi(settings_menu->get_item_by_id(MN_ID_WIDTH).input.c_str()),
325               atoi(settings_menu->get_item_by_id(MN_ID_HEIGHT).input.c_str()));
326         foregrounds->resize(atoi(settings_menu->get_item_by_id(MN_ID_WIDTH).input.c_str()),
327               atoi(settings_menu->get_item_by_id(MN_ID_HEIGHT).input.c_str()));
328         backgrounds->resize(atoi(settings_menu->get_item_by_id(MN_ID_WIDTH).input.c_str()),
329               atoi(settings_menu->get_item_by_id(MN_ID_HEIGHT).input.c_str()));
330
331         Menu::set_current(0);
332         }
333       }
334     }
335   // check for events in buttons
336   else if(tiles_board->event(event))
337     {
338     std::vector <int> vector;
339     vector.push_back(tiles_board->selected_id());
340
341     selection.clear();
342     selection.push_back(vector);
343     continue;
344     }
345   else if(tiles_layer->event(event))
346     {
347     cur_layer = tiles_layer->selected_id();
348     continue;
349     }
350   else if(level_options->event(event))
351     {
352     switch(level_options->selected_id())
353       {
354       case BT_LEVEL_SAVE:
355         save_level();
356         break;
357       case BT_LEVEL_TEST:
358         test_level();
359         break;
360       case BT_LEVEL_SETUP:
361         Menu::set_current(settings_menu);
362         break;
363       case BT_NEXT_LEVEL:
364         if(level_nb + 1 < level_subset->get_num_levels())
365           load_level(level_nb + 1);
366         else
367           {
368           char str[1024];
369           sprintf(str,_("Level %d doesn't exist. Create it?"), level_nb + 2);
370           if(confirm_dialog(NULL, str))
371             {
372             level_subset->add_level("new_level.stl");
373             Level* newlevel = new Level();
374             newlevel->add_sector(create_sector("main", 25, 19));
375             newlevel->save(level_subset->get_level_filename(level_nb + 1));
376             level_subset->save();
377             load_level(level_nb + 1);
378             }
379           }
380         break;
381       case BT_PREVIOUS_LEVEL:
382         if(level_nb - 1 >= 0)
383           load_level(level_nb - 1);
384         break;
385       case BT_NEXT_SECTOR:
386 std::cerr << "next sector.\n";
387 std::cerr << "total sectors: " << level->get_total_sectors() << std::endl;
388         load_sector(level->get_next_sector(sector));
389         break;
390       case BT_PREVIOUS_SECTOR:
391 std::cerr << "previous sector.\n";
392         load_sector(level->get_previous_sector(sector));
393         break;
394       }
395     level_options->set_unselected();
396     continue;
397     }
398   else
399     {
400     switch(event.type)
401       {
402       case SDL_MOUSEMOTION:
403         mouse_moved = true;
404         if(SDL_GetMouseState(NULL, NULL)&SDL_BUTTON(SDL_BUTTON_RIGHT))
405           {  // movement like in strategy games
406           scroll.x += -1 * event.motion.xrel;
407           scroll.y += -1 * event.motion.yrel;
408           }
409         break;
410
411       case SDL_MOUSEBUTTONDOWN:
412         mouse_moved = true;
413         if(event.button.button == SDL_BUTTON_LEFT)
414           left_button = true;
415         else if(event.button.button == SDL_BUTTON_MIDDLE)
416           {
417           middle_button = true;
418           selection_ini = Vector(event.button.x, event.button.y);
419           }
420         break;
421
422       case SDL_MOUSEBUTTONUP:
423         mouse_moved = true;
424         if(event.button.button == SDL_BUTTON_LEFT)
425           left_button = false;
426         else if(event.button.button == SDL_BUTTON_MIDDLE)
427           {
428           middle_button = false;
429           selection_end = Vector(event.button.x, event.button.y);
430
431           if(selection_end.x < selection_ini.x)
432             {
433             float t = selection_ini.x;
434             selection_ini.x = selection_end.x;
435             selection_end.x = t;
436             }
437           if(selection_end.y < selection_ini.y)
438             {
439             float t = selection_ini.y;
440             selection_ini.y = selection_end.y;
441             selection_end.y = t;
442             }
443
444           selection.clear();
445           std::vector <int> vector;
446
447           TileMap* tilemap = 0;
448           if(cur_layer == LAYER_FOREGROUNDTILES)
449             tilemap = foregrounds;
450           else if(cur_layer == LAYER_TILES)
451             tilemap = solids;
452           else if(cur_layer == LAYER_BACKGROUNDTILES)
453             tilemap = backgrounds;
454
455           for(int x = 0; x < (int)((selection_end.x - selection_ini.x)*zoom / 32) + 1; x++)
456             {
457             vector.clear();
458             for(int y = 0; y < (int)((selection_end.y - selection_ini.y)*zoom / 32) + 1; y++)
459               {
460               vector.push_back(tilemap->get_tile(x +
461                (int)(((selection_ini.x+scroll.x)*zoom)/32),
462                y + (int)(((selection_ini.y+scroll.y)*zoom)/32))->id);
463               }
464             selection.push_back(vector);
465             }
466           }
467         break;
468
469       case SDL_KEYDOWN:   // key pressed
470         switch(event.key.keysym.sym)
471           {
472           case SDLK_ESCAPE:
473             Menu::set_current(main_menu);
474             break;
475           /* scrolling related events: */
476           case SDLK_HOME:
477             scroll.x = 0;
478             break;
479           case SDLK_END:
480             scroll.x = sector->solids->get_height()*32 - screen->w;
481             break;
482           case SDLK_LEFT:
483             scroll.x -= 80;
484             break;
485           case SDLK_RIGHT:
486             scroll.x += 80;
487             break;
488           case SDLK_UP:
489             scroll.y -= 80;
490             break;
491           case SDLK_DOWN:
492             scroll.y += 80;
493             break;
494           case SDLK_PAGEUP:
495             scroll.x -= 450;
496             break;
497           case SDLK_PAGEDOWN:
498             scroll.x += 450;
499             break;
500           case SDLK_PLUS:
501           case SDLK_KP_PLUS:
502             zoom += 0.10;
503             break;
504           case SDLK_MINUS:
505           case SDLK_KP_MINUS:
506             zoom -= 0.10;
507             break;
508
509           case SDLK_F1:
510             show_help();
511             break;
512           case SDLK_F2:
513             show_grid = !show_grid;
514             break;
515           default:
516             break;
517           }
518         break;
519
520       case SDL_QUIT:   // window closed
521         done = true;
522         break;
523
524         default:
525           break;
526       }
527     }
528   }
529 }
530
531 void LevelEditor::action()
532 {
533 mouse_cursor->set_state(MC_NORMAL);
534 if(tiles_board->is_hover() || tiles_layer->is_hover() || level_options->is_hover())
535   mouse_cursor->set_state(MC_LINK);
536
537 if(sector)
538   {
539   // don't scroll before the start or after the level's end
540   float width = sector->solids->get_width() * 32;
541   float height = sector->solids->get_height() * 32;
542
543   if(scroll.x < -screen->w/2)
544     scroll.x = -screen->w/2;
545   if(scroll.x > width - screen->w/2)
546     scroll.x = width - screen->w/2;
547   if(scroll.y < -screen->h/2)
548     scroll.y = -screen->h/2;
549   if(scroll.y > height - screen->h/2)
550     scroll.y = height - screen->h/2;
551
552   // set camera translation, since BadGuys like it
553   sector->camera->set_scrolling((int)scroll.x, (int)scroll.y);
554
555   if(left_button && mouse_moved)
556     for(unsigned int x = 0; x < selection.size(); x++)
557       for(unsigned int y = 0; y < selection[x].size(); y++)
558         change((int)(scroll.x + event.button.x) + (x*32),
559              (int)(scroll.y + event.button.y) + (y*32), selection[x][y], 
560              cur_layer);
561   }
562 }
563
564 #define FADING_TIME .6
565
566 void LevelEditor::draw(DrawingContext& context)
567 {
568 context.draw_text(white_text, _("Level Editor"), Vector(10, 5), LEFT_ALLIGN, LAYER_GUI);
569 mouse_cursor->draw(context);
570
571 // draw a filled background
572 context.draw_filled_rect(Vector(0,0), Vector(screen->w,screen->h), Color(60,60,60), LAYER_BACKGROUND0-1);
573
574 if(level_name_timer.check())
575   {
576   context.push_transform();
577   if(level_name_timer.get_timeleft() < FADING_TIME)
578     context.set_alpha(int(level_name_timer.get_timeleft() * 255 / FADING_TIME));
579
580   context.draw_text(gold_text, level->name, Vector(screen->w/2, 30), CENTER_ALLIGN, LAYER_GUI);
581   if(level_nb != -1)
582     {
583     char str[128];
584     sprintf(str, "%i/%i", level_nb+1, level_subset->get_num_levels());
585     context.draw_text(gold_text, str, Vector(screen->w/2, 50), CENTER_ALLIGN, LAYER_GUI);
586     }
587
588   context.pop_transform();
589   }
590 if(sector)
591   context.draw_text(white_small_text, _("F1 for help"), Vector(5, 510), LEFT_ALLIGN, LAYER_GUI-10);
592 else
593   context.draw_text(white_small_text, _("Choose a level subset"), Vector(5, 510), LEFT_ALLIGN, LAYER_GUI-10);
594
595 Menu* menu = Menu::current();
596 if(menu)
597   menu->draw(context);
598 else
599   {
600   tiles_board->draw(context);
601   tiles_layer->draw(context);
602   level_options->draw(context);
603   }
604
605 // draw selection
606 if(sector)
607   {
608   if(!middle_button)
609     {
610     context.set_drawing_effect(SEMI_TRANSPARENT);
611
612     if(selection.size())
613       {
614       if(selection[0][0] == 0 && selection.size() == 1)
615           context.draw_surface(img_rubber_bt, Vector(event.button.x - 8,
616           event.button.y - 8), LAYER_GUI-2);
617       else if(selection[0][0] < 0)
618         {
619         int id = selection[0][0];
620
621 #if 0
622         if(id == OBJ_TRAMPOLINE)
623           context.draw_surface(img_trampoline[0].get_frame(0), Vector(event.button.x - 8,
624           event.button.y - 8), LAYER_GUI-2);
625         else if(id == OBJ_FLYING_PLATFORM)
626           context.draw_surface(img_flying_platform->get_frame(0), Vector(event.button.x - 8,
627           event.button.y - 8), LAYER_GUI-2);
628         else
629 #endif
630         if(id == OBJ_DOOR)
631           /*context.draw_surface(door->get_frame(0), Vector(event.button.x - 8,
632           event.button.y - 8), LAYER_GUI-2);*/
633           ;
634         else
635           {
636 #if 0
637           BadGuyKind kind = BadGuyKind((-id)-1);
638           BadGuy badguy(kind, 0,0);
639           badguy.activate(LEFT);
640           Surface *img = badguy.get_image();
641
642           context.draw_surface(img, Vector(event.button.x - 8,
643           event.button.y - 8), LAYER_GUI-2);
644 #endif
645           }
646         }
647       else
648         {
649         TileManager* tilemanager = TileManager::instance();
650         for(unsigned int x = 0; x < selection.size(); x++)
651           for(unsigned int y = 0; y < selection[x].size(); y++) {
652             const Tile* tile = tilemanager->get(selection[x][y]);
653             tile->draw(context,
654                 Vector(event.button.x + x*32 - 8, event.button.y + y*32 - 8),
655                 LAYER_GUI-2);
656           }
657         }
658       }
659     context.set_drawing_effect(NONE_EFFECT);
660     }
661   else
662     context.draw_filled_rect(Vector(std::min((int)selection_ini.x, (int)event.button.x)*zoom,
663                    std::min((int)selection_ini.y, (int)event.button.y))*zoom,
664                    Vector(abs(event.button.x - (int)selection_ini.x)*zoom,
665                    abs(event.button.y - (int)selection_ini.y)*zoom),
666                    Color(170,255,170,128), LAYER_GUI-2);
667
668   if(show_grid)
669     {
670     for(int x = 0; x < screen->w / (32*zoom); x++)
671       {
672       int pos = (int)(x*32*zoom) - ((int)scroll.x % 32);
673       context.draw_filled_rect(Vector (pos, 0), Vector(1, screen->h),
674                 Color(225, 225, 225), LAYER_GUI-50);
675       }
676     for(int y = 0; y < screen->h / (32*zoom); y++)
677       {
678       int pos = (int)(y*32*zoom) - ((int)scroll.y % 32);
679       context.draw_filled_rect(Vector (0, pos), Vector(screen->w, 1),
680                 Color(225, 225, 225), LAYER_GUI-50);
681       }
682     }
683
684   context.push_transform();
685   context.set_translation(scroll);
686   context.set_zooming(zoom);
687
688   for(Sector::GameObjects::iterator i = sector->gameobjects.begin(); i != sector->gameobjects.end(); ++i)
689     {
690     TileMap* tilemap = dynamic_cast<TileMap*> (*i);
691     if(tilemap)
692       {  // draw the non-selected tiles semi-transparently
693       context.push_transform();
694
695       if(tilemap->get_layer() != cur_layer)
696         context.set_drawing_effect(SEMI_TRANSPARENT);
697       (*i)->draw(context);
698
699       context.pop_transform();
700       continue;
701       }
702     Background* background = dynamic_cast<Background*> (*i);
703     if(background)
704       {  // don't resize background
705       context.push_transform();
706       context.set_zooming(1.0);
707       (*i)->draw(context);
708       context.pop_transform();
709       }
710     else
711       (*i)->draw(context);
712     }
713
714   context.pop_transform();
715   }
716 else
717   context.draw_filled_rect(Vector(0,0), Vector(screen->w,screen->h),Color(0,0,0), LAYER_BACKGROUND0);
718
719 context.do_drawing();
720 }
721
722 void LevelEditor::load_level_subset(std::string filename)
723 {
724 delete level_subset;
725 level_subset = new LevelSubset();
726 level_subset->load(filename.c_str());
727 load_level(0);
728 }
729
730 void LevelEditor::load_level(std::string filename)
731 {
732 if(level_changed)
733   {
734   if(confirm_dialog(NULL, _("Level not saved. Wanna to?")))
735     save_level();
736   else
737     return;
738   }
739
740 level_filename = filename;
741
742 delete level;
743 level = new Level();
744 level->load(filename);
745
746 load_sector("main");
747 level_name_timer.start(3000);
748 scroll.x = scroll.y = 0;
749 level_changed = false;
750
751 settings_menu->get_item_by_id(MN_ID_NAME).change_input(level->name.c_str());
752 settings_menu->get_item_by_id(MN_ID_AUTHOR).change_input(level->author.c_str());
753 }
754
755 void LevelEditor::load_level(int nb)
756 {
757 if(level_changed)
758   {
759   if(confirm_dialog(NULL, _("Level not saved. Wanna to?")))
760     save_level();
761   else
762     return;
763   }
764
765 level_nb = nb;
766 level_filename = level_subset->get_level_filename(level_nb);
767
768 load_level(level_filename);
769 }
770
771 void LevelEditor::load_sector(std::string name)
772 {
773 sector_name = name;
774 sector = level->get_sector(sector_name);
775 if(!sector)
776   Termination::abort("Level has no " + sector_name + " sector.", "");
777
778 load_sector(sector);
779 }
780
781 void LevelEditor::load_sector(Sector* sector_)
782 {
783 if(sector_ == NULL)
784   {
785   if(!confirm_dialog(NULL, _("No more sectors exist. Create another?")))
786     return;
787   sector_ = create_sector("new_sector",25,19);
788   level->add_sector(sector_);
789   }
790
791 sector = sector_;
792
793 /* Load sector stuff */
794
795 sector->update_game_objects();
796
797 foregrounds = solids = backgrounds = 0;
798 /* Point foregrounds, backgrounds, solids to its layer */
799 for(Sector::GameObjects::iterator i = sector->gameobjects.begin(); i != sector->gameobjects.end(); i++)
800   {
801 #if 0
802   BadGuy* badguy = dynamic_cast<BadGuy*> (*i);
803   if(badguy)
804     badguy->activate(LEFT);
805 #endif
806
807   TileMap* tilemap = dynamic_cast<TileMap*> (*i);
808   if(tilemap)
809     {
810     if(tilemap->get_layer() == LAYER_FOREGROUNDTILES)
811       foregrounds = tilemap;
812     else if(tilemap->get_layer() == LAYER_TILES)
813       solids = tilemap;
814     else if(tilemap->get_layer() == LAYER_BACKGROUNDTILES)
815       backgrounds = tilemap;
816     }
817   }
818
819 if(!foregrounds)
820   {
821   TileMap* tilemap = new TileMap(LAYER_FOREGROUNDTILES, false, solids->get_width(), solids->get_height());
822   sector->add_object(tilemap);
823   sector->update_game_objects();
824   }
825 if(!backgrounds)
826   {
827   TileMap* tilemap = new TileMap(LAYER_BACKGROUNDTILES, false, solids->get_width(), solids->get_height());
828   sector->add_object(tilemap);
829   sector->update_game_objects();
830   }
831
832 char str[64];
833 sprintf(str, "%i", solids->get_width());
834 settings_menu->get_item_by_id(MN_ID_WIDTH).change_input(str);
835 sprintf(str, "%i", solids->get_height());
836 settings_menu->get_item_by_id(MN_ID_HEIGHT).change_input(str);
837 }
838
839 void LevelEditor::save_level()
840 {
841 level->save(level_filename);
842 level_changed = false;
843 }
844
845 void LevelEditor::test_level()
846 {
847   if(level_changed) {
848     if(confirm_dialog(NULL, _("Level not saved. Wanna to?")))
849       save_level();
850     else
851       return;
852   }
853
854   GameSession session(level_filename, ST_GL_TEST);
855   session.run();
856   //  player_status.reset();
857   SoundManager::get()->halt_music();
858 }
859
860 void LevelEditor::change(int x, int y, int newtile, int layer)
861 {  
862   (void) layer;
863   // find the tilemap of the current layer, and then change the tile
864   if(x < 0 || (unsigned int)x >= sector->solids->get_width()*32 ||
865       y < 0 || (unsigned int)y >= sector->solids->get_height()*32)
866     return;
867
868   level_changed = true;
869   
870   if(zoom != 1)
871   {  // no need to do this for normal view (no zoom)
872     x = (int)(x * (zoom*32) / 32);
873     y = (int)(y * (zoom*32) / 32);
874   }
875
876   if(newtile < 0)  // add object
877   {
878     // remove an active tile or object that might be there
879     change(x, y, 0, LAYER_TILES);
880
881 #if 0
882     if(newtile == OBJ_TRAMPOLINE)
883       sector->add_object(new Trampoline(x, y));
884     else if(newtile == OBJ_FLYING_PLATFORM)
885       sector->add_object(new FlyingPlatform(x, y));
886     else
887       if(newtile == OBJ_DOOR)
888         sector->add_object(new Door(x, y));
889       else
890         sector->add_bad_guy(x, y, BadGuyKind((-newtile)-1), true);
891 #endif
892
893     sector->update_game_objects();
894   } else if(cur_layer == LAYER_FOREGROUNDTILES) {
895     foregrounds->change(x/32, y/32, newtile);
896   } else if(cur_layer == LAYER_TILES) {
897     // remove a bad guy if it's there
898     // we /32 in order to round numbers
899     for(Sector::GameObjects::iterator i = sector->gameobjects.begin();
900         i < sector->gameobjects.end(); i++) {
901 #if 0
902       BadGuy* badguy = dynamic_cast<BadGuy*> (*i);
903       if(badguy)
904         if((int)badguy->base.x/32 == x/32 && (int)badguy->base.y/32 == y/32)
905           sector->gameobjects.erase(i);
906 #endif
907 #if 0
908       Trampoline* trampoline = dynamic_cast<Trampoline*> (*i);
909       if(trampoline)
910       {
911         if((int)trampoline->base.x/32 == x/32 && (int)trampoline->base.y/32 == y/32)
912           sector->gameobjects.erase(i);
913       }
914       FlyingPlatform* flying_platform = dynamic_cast<FlyingPlatform*> (*i);
915       if(flying_platform)
916         if((int)flying_platform->base.x/32 == x/32 && (int)flying_platform->base.y/32 == y/32)
917           sector->gameobjects.erase(i);
918 #endif
919 #if 0
920       Door* door = dynamic_cast<Door*> (*i);
921       if(door)
922         if((int)door->get_area().x/32 == x/32 && (int)door->get_area().y/32 == y/32)
923           sector->gameobjects.erase(i);
924 #endif
925     }
926     sector->update_game_objects();
927     solids->change(x/32, y/32, newtile);
928   } else if(cur_layer == LAYER_BACKGROUNDTILES)
929     backgrounds->change(x/32, y/32, newtile);
930 }
931
932 void LevelEditor::show_help()
933 {
934 DrawingContext context;
935
936 bool show_grid_t = show_grid;
937 show_grid = false;
938 mouse_cursor->set_state(MC_HIDE);
939
940
941 char str[1024];
942 const char *text1[] = {
943          _("This is the built-in level editor. Its aim is to be intuitive\n"
944          "and simple to use, so it should be pretty straightforward.\n"
945          "\n"
946          "To open a level, first you'll have to select a level subset from\n"
947          "the menu (or create your own).\n"
948          "A level subset is basically a collection of levels.\n"
949          "They can then be played from the Contrib menu.\n"
950          "\n"
951          "To access the menu from the level editor, just press Esc.\n"
952          "\n"
953          "You are currently looking at the level. To scroll it, just\n"
954          "press the right mouse button and drag the mouse. It will move like\n"
955          "a strategy game.\n"
956          "You can also use the arrow keys and Page Up/Down.\n"
957          "\n"
958          "'+' and '-' keys can be used to zoom the level in/out.\n"
959          "\n"
960          "You probably already noticed those floating groups of buttons.\n"
961          "Each one serves a different purpose. To select a certain button\n"
962          "just press the Left mouse button on it. A few buttons have key\n"
963          "shortcuts. You can find them by pressing the Right mouse button on\n"
964          "a button. That will also show what that button does.\n"
965          "Groups of buttons can also be moved around by just dragging them,\n"
966          "while pressing the Left mouse button.\n"
967          "\n"
968          "Let's learn a bit of what each group of buttons does, shall we?\n"
969          "\n"
970          "To starting putting tiles and objects around use the bigger group\n"
971          "of buttons. Each button is a different tile. To put it on the level,\n"
972          "just press it and then left click in the level.\n"
973          "You can also copy tiles from the level by using the middle mouse button.\n"
974          "Use the mouse wheel to scroll that group of buttons. You will find\n"
975          "enemies and game objects in the bottom.\n")
976                 };
977
978 const char *text2[] = {
979          _("The Foreground/Interactive/Background buttons may be used to\n"
980          "see and edit the respective layer. Levels have three tiles layers:\n"
981          "Foreground - tiles are drawn on top of everything and have no contact\n"
982          "with the player.\n"
983          "Interactive - these are the tiles that have contact with the player.\n"
984          "Background - tiles are drawn underneath everything and have no contact\n"
985          "with the player.\n"
986          "The unselected layers will be drawn semi-transparently.\n"
987          "\n"
988          "Last, but not least, the group of buttons that's left serves\n"
989          "to do related actions with the level.\n"
990          "From left to right:\n"
991          "Mini arrows - can be used to choose other sectors.\n"
992          "Sectors are mini-levels, so to speak, that can be accessed using a door.\n"
993          "Big arrows - choose other level in the same level subset.\n"
994          "Diskette - save the level\n"
995          "Tux - test the level\n"
996          "Tools - set a few settings for the level, including resizing it.\n"
997          "\n"
998          "We have reached the end of this Howto.\n"
999          "\n"
1000          "Don't forget to send us a few cool levels. :)\n"
1001          "\n"
1002          "Enjoy,\n"
1003          "  SuperTux development team\n"
1004          "\n"
1005          "PS: If you are looking for something more powerful, you might like to\n"
1006          "try FlexLay. FlexLay is a level editor that supports several games,\n"
1007          "including SuperTux. It is an independent project.\n"
1008          "Webpage: http://pingus.seul.org/~grumbel/flexlay/")
1009                 };
1010
1011 const char **text[] = { text1, text2 };
1012
1013
1014 bool done;
1015 for(unsigned int i = 0; i < sizeof(text) / sizeof(text[0]); i++)
1016   {
1017   draw(context);
1018
1019   context.draw_text(blue_text, _("- Level Editor's Help -"), Vector(screen->w/2, 60), CENTER_ALLIGN, LAYER_GUI);
1020
1021   context.draw_text(white_small_text, *text[i], Vector(20, 120), LEFT_ALLIGN, LAYER_GUI);
1022
1023   sprintf(str,_("Press any key to continue - Page %d/%d"), i+1, sizeof(text) / sizeof(text[0]));
1024   context.draw_text(gold_text, str, Vector(screen->w/2, screen->h-60), CENTER_ALLIGN, LAYER_GUI);
1025
1026   context.do_drawing();
1027
1028   done = false;
1029
1030   while(!done)
1031     {
1032     done = wait_for_event(event);
1033     SDL_Delay(50);
1034     }
1035   }
1036
1037 show_grid = show_grid_t;
1038 mouse_cursor->set_state(MC_NORMAL);
1039 }
1040
1041 Sector*
1042 LevelEditor::create_sector(const std::string& name, size_t width, size_t height)
1043 {
1044   Sector* sector = new Sector;
1045   sector->set_name(name);
1046   
1047   sector->add_object(new TileMap(LAYER_BACKGROUNDTILES, false, width, height));
1048   sector->add_object(new TileMap(LAYER_TILES, true, width, height));
1049   sector->add_object(new TileMap(LAYER_FOREGROUNDTILES, false, width, height));
1050   sector->add_object(new Camera(sector));
1051   sector->update_game_objects();
1052   
1053   return sector;
1054 }
1055