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