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