-converted remaining classes to GameObject
[supertux.git] / src / leveleditor.cpp
1 //  $Id$
2 //
3 //  SuperTux
4 //  Copyright (C) 2003 Ricardo Cruz <rick2@aeiou.pt>
5 //  Copyright (C) 2003 Tobias Glaesser <tobi.web@gmx.de>
6 //
7 //  This program is free software; you can redistribute it and/or
8 //  modify it under the terms of the GNU General Public License
9 //  as published by the Free Software Foundation; either version 2
10 //  of the License, or (at your option) any later version.
11 //
12 //  This program is distributed in the hope that it will be useful,
13 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
14 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 //  GNU General Public License for more details.
16 //
17 //  You should have received a copy of the GNU General Public License
18 //  along with this program; if not, write to the Free Software
19 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20
21 #include <map>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <math.h>
26 #include <errno.h>
27 #include <unistd.h>
28 #include <SDL.h>
29 #include <SDL_image.h>
30 #include "leveleditor.h"
31
32 #include "screen.h"
33 #include "defines.h"
34 #include "globals.h"
35 #include "setup.h"
36 #include "menu.h"
37 #include "level.h"
38 #include "gameloop.h"
39 #include "badguy.h"
40 #include "scene.h"
41 #include "button.h"
42 #include "tile.h"
43 #include "resources.h"
44 #include "music_manager.h"
45 #include "display_manager.h"
46
47 /* definitions to aid development */
48
49 /* definitions that affect gameplay */
50 #define KEY_CURSOR_SPEED 32
51 #define KEY_CURSOR_FASTSPEED 64
52
53 /* when pagedown/up pressed speed:*/
54 #define PAGE_CURSOR_SPEED 13*32
55
56 #define MOUSE_LEFT_MARGIN 80
57 #define MOUSE_RIGHT_MARGIN (560-32)
58
59 /* scolling speed */
60 #define KEYBOARD_SPEED 140
61 #define MOUSE_SPEED    40
62
63 /* look */
64 #define SELECT_W 2 // size of the selections lines
65 #define SELECT_CLR 0, 255, 0, 255  // lines color (R, G, B, A)
66
67 /* own declerations */
68 /* crutial ones (main loop) */
69 int le_init();
70 void le_quit();
71 int le_load_level_subset(char *filename);
72 void le_drawlevel();
73 void le_drawinterface();
74 void le_checkevents();
75 void le_change(float x, float y, int tm, unsigned int c);
76 void le_testlevel();
77 void le_showhelp();
78 void le_set_defaults(void);
79 void le_activate_bad_guys(void);
80 void le_goto_level(int levelnb);
81 void le_highlight_selection();
82
83 void apply_level_settings_menu();
84 void update_subset_settings_menu();
85 void save_subset_settings_menu();
86
87 struct TileOrObject
88 {
89   TileOrObject() : tile(0), obj(NULL) { is_tile = true; };
90
91   void Tile(unsigned int set_to) { tile = set_to; is_tile = true; }
92   void Object(GameObject* pobj) { obj = pobj; is_tile = false; }
93   //Returns true for a tile
94   bool IsTile() { return is_tile; };
95   //Returns true for a GameObject
96   bool IsObject() { return !is_tile; };
97
98
99   void Init() { tile = 0; obj = NULL; is_tile = true; };
100
101   bool is_tile; //true for tile (false for object)
102   unsigned int tile;
103   GameObject* obj;
104 };
105
106 /* leveleditor internals */
107 static string_list_type level_subsets;
108 static bool le_level_changed;  /* if changes, ask for saving, when quiting*/
109 static bool show_minimap;
110 static bool show_selections;
111 static bool le_help_shown;
112 static int pos_x, pos_y, cursor_x, cursor_y;
113 static int le_level;
114 static World* le_world;
115 static LevelSubset* le_level_subset;
116 static int le_show_grid;
117 static int le_frame;
118 static Surface* le_selection;
119 static int done;
120 static TileOrObject le_current;
121 static bool le_mouse_pressed[2];
122 static bool le_mouse_clicked[2];
123 static Button* le_save_level_bt;
124 static Button* le_exit_bt;
125 static Button* le_test_level_bt;
126 static Button* le_next_level_bt;
127 static Button* le_previous_level_bt;
128 static Button* le_move_right_bt;
129 static Button* le_move_left_bt;
130 static Button* le_move_up_bt;
131 static Button* le_move_down_bt;
132 static Button* le_rubber_bt;
133 static Button* le_select_mode_one_bt;
134 static Button* le_select_mode_two_bt;
135 static Button* le_settings_bt;
136 static Button* le_tilegroup_bt;
137 static Button* le_objects_bt;
138 static Button* le_object_select_bt;
139 static Button* le_object_properties_bt;
140 static ButtonPanel* le_tilemap_panel;
141 static Menu* leveleditor_menu;
142 static Menu* subset_load_menu;
143 static Menu* subset_new_menu;
144 static Menu* subset_settings_menu;
145 static Menu* level_settings_menu;
146 static Menu* select_tilegroup_menu;
147 static Menu* select_objects_menu;
148 static Timer select_tilegroup_menu_effect;
149 static Timer select_objects_menu_effect;
150 static Timer display_level_info;
151 typedef std::map<std::string, ButtonPanel*> ButtonPanelMap;
152 static ButtonPanelMap tilegroups_map;
153 static ButtonPanelMap objects_map;
154 static std::string cur_tilegroup;
155 static std::string cur_objects;
156 static MouseCursor* mouse_select_object;
157 static MovingObject* selected_game_object;
158
159 static square selection;
160 static SelectionMode le_selection_mode;
161 static SDL_Event event;
162 TileMapType active_tm;
163
164 int leveleditor(char* filename)
165 {
166   int last_time, now_time, i;
167
168   le_level = 1;
169
170   if(le_init() != 0)
171     return 1;
172
173   /* Clear screen: */
174
175   clearscreen(0, 0, 0);
176   updatescreen();
177
178   music_manager->halt_music();
179
180   while (SDL_PollEvent(&event))
181   {}
182
183   if(filename != NULL)
184     if(le_load_level_subset(filename))
185       return 1;
186
187   while(true)
188   {
189     last_time = SDL_GetTicks();
190     le_frame++;
191
192     le_checkevents();
193
194     if(Menu::current() == select_tilegroup_menu)
195     {
196       if(select_tilegroup_menu_effect.check())
197       {
198         select_tilegroup_menu->set_pos(screen->w - 64 + select_tilegroup_menu_effect.get_left(),
199                                        66,-0.5,0.5);
200       }
201       else
202         select_tilegroup_menu->set_pos(screen->w - 64,66,-0.5,0.5);
203     }
204     else if(Menu::current() == select_objects_menu)
205     {
206       if(select_objects_menu_effect.check())
207       {
208         select_objects_menu->set_pos(screen->w - 64 + select_objects_menu_effect.get_left(),82,-0.5,0.5);
209       }
210       else
211         select_objects_menu->set_pos(screen->w - 64,82,-0.5,0.5);
212     }
213
214     if(le_world != NULL)
215     {
216       /* making events results to be in order */
217
218       /* draw the level */
219       le_drawlevel();
220     }
221     else
222       clearscreen(0, 0, 0);
223
224     /* draw editor interface */
225     le_drawinterface();
226
227     Menu* menu = Menu::current();
228     if(menu)
229     {
230       menu->draw();
231       menu->action();
232
233       if(menu == leveleditor_menu)
234       {
235         switch (leveleditor_menu->check())
236         {
237         case MNID_RETURNLEVELEDITOR:
238           if(le_world != NULL)
239             Menu::set_current(0);
240           else
241             Menu::set_current(leveleditor_menu);
242           break;
243         case MNID_SUBSETSETTINGS:
244           update_subset_settings_menu();
245           break;
246         case MNID_QUITLEVELEDITOR:
247           done = 1;
248           break;
249         }
250       }
251       else if(menu == level_settings_menu)
252       {
253         switch (level_settings_menu->check())
254         {
255         case MNID_APPLY:
256           apply_level_settings_menu();
257           Menu::set_current(NULL);
258           break;
259
260         default:
261           break;
262         }
263       }
264       else if(menu == select_tilegroup_menu)
265       {
266         int it = -1;
267         switch (it = select_tilegroup_menu->check())
268         {
269         default:
270           if(it >= 0)
271           {
272             cur_tilegroup = select_tilegroup_menu->get_item_by_id(it).text;
273             Menu::set_current(0);
274             cur_objects = "";
275
276           }
277           break;
278         }
279       }
280       else if(menu == select_objects_menu)
281       {
282         int it = -1;
283         switch (it = select_objects_menu->check())
284         {
285         default:
286           if(it >= 0)
287           {
288             cur_objects = select_objects_menu->get_item_by_id(it).text;
289             cur_tilegroup = "";
290
291             Menu::set_current(0);
292           }
293           break;
294         }
295       }
296       else if(menu == subset_load_menu)
297       {
298         switch (i = subset_load_menu->check())
299         {
300         case 0:
301           break;
302         default:
303           if(i >= 1)
304           {
305             if(le_load_level_subset(level_subsets.item[i-1]))
306               return 1;
307           }
308           break;
309         }
310       }
311       else if(menu == subset_new_menu)
312       {
313         if(subset_new_menu->item[2].input[0] == '\0')
314           subset_new_menu->item[3].kind = MN_DEACTIVE;
315         else
316         {
317           subset_new_menu->item[3].kind = MN_ACTION;
318
319           switch (i = subset_new_menu->check())
320           {
321           case MNID_CREATESUBSET:
322             LevelSubset::create(subset_new_menu->get_item_by_id(MNID_SUBSETNAME).input);
323             le_level_subset->load(subset_new_menu->get_item_by_id(MNID_SUBSETNAME).input);
324             leveleditor_menu->get_item_by_id(MNID_SUBSETSETTINGS).kind = MN_GOTO;
325             le_goto_level(1);
326             subset_new_menu->get_item_by_id(MNID_SUBSETNAME).change_input("");
327
328             Menu::set_current(subset_settings_menu);
329             break;
330           }
331         }
332       }
333       else if(menu == subset_settings_menu)
334       {
335         if(le_level_subset->title.compare(subset_settings_menu->get_item_by_id(MNID_SUBSETTITLE).input) == 0 && le_level_subset->description.compare(subset_settings_menu->get_item_by_id(MNID_SUBSETDESCRIPTION).input) == 0  )
336           subset_settings_menu->get_item_by_id(MNID_SUBSETSAVECHANGES).kind = MN_DEACTIVE;
337         else
338           subset_settings_menu->get_item_by_id(MNID_SUBSETSAVECHANGES).kind = MN_ACTION;
339
340         switch (i = subset_settings_menu->check())
341         {
342         case MNID_SUBSETSAVECHANGES:
343           save_subset_settings_menu();
344           Menu::set_current(leveleditor_menu);
345           break;
346         }
347       }
348     }
349
350     MouseCursor::current()->draw();
351
352     if(done)
353     {
354       le_quit();
355       return 0;
356     }
357
358     ++global_frame_counter;
359
360     SDL_Delay(25);
361     now_time = SDL_GetTicks();
362     if (now_time < last_time + FPS)
363       SDL_Delay(last_time + FPS - now_time);    /* delay some time */
364
365     flipscreen();
366   }
367
368   return done;
369 }
370
371 int le_load_level_subset(char *filename)
372 {
373   le_level_subset->load(filename);
374   leveleditor_menu->get_item_by_id(MNID_SUBSETSETTINGS).kind = MN_GOTO;
375   le_level = 1;
376   le_goto_level(1);
377
378   //GameSession* session = new GameSession(datadir + "/levels/" + le_level_subset->name + "/level1.stl", 0, ST_GL_DEMO_GAME);
379
380   Menu::set_current(NULL);
381
382   return 0;
383 }
384
385 void le_init_menus()
386 {
387   int i;
388
389   leveleditor_menu = new Menu();
390   subset_load_menu = new Menu();
391   subset_new_menu  = new Menu();
392   subset_settings_menu = new Menu();
393   level_settings_menu  = new Menu();
394   select_tilegroup_menu  = new Menu();
395   select_objects_menu = new Menu();
396
397   leveleditor_menu->additem(MN_LABEL,"Level Editor Menu",0,0);
398   leveleditor_menu->additem(MN_HL,"",0,0);
399   leveleditor_menu->additem(MN_ACTION,"Return To Level Editor",0,0,MNID_RETURNLEVELEDITOR);
400   leveleditor_menu->additem(MN_DEACTIVE,"Level Subset Settings",0,subset_settings_menu,MNID_SUBSETSETTINGS);
401   leveleditor_menu->additem(MN_GOTO,"Load Level Subset",0,subset_load_menu);
402   leveleditor_menu->additem(MN_GOTO,"New Level Subset",0,subset_new_menu);
403   leveleditor_menu->additem(MN_HL,"",0,0);
404   leveleditor_menu->additem(MN_ACTION,"Quit Level Editor",0,0,MNID_QUITLEVELEDITOR);
405
406   Menu::set_current(leveleditor_menu);
407
408   subset_load_menu->additem(MN_LABEL, "Load Level Subset", 0, 0);
409   subset_load_menu->additem(MN_HL, "", 0, 0);
410
411   for(i = 0; i < level_subsets.num_items; ++i)
412   {
413     subset_load_menu->additem(MN_ACTION,level_subsets.item[i],0,0, i+1);
414   }
415   subset_load_menu->additem(MN_HL,"",0,0);
416   subset_load_menu->additem(MN_BACK,"Back",0,0);
417
418   subset_new_menu->additem(MN_LABEL,"New Level Subset",0,0);
419   subset_new_menu->additem(MN_HL,"",0,0);
420   subset_new_menu->additem(MN_TEXTFIELD,"Enter Name",0,0,MNID_SUBSETNAME);
421   subset_new_menu->additem(MN_ACTION,"Create",0,0, MNID_CREATESUBSET);
422   subset_new_menu->additem(MN_HL,"",0,0);
423   subset_new_menu->additem(MN_BACK,"Back",0,0);
424
425   subset_settings_menu->additem(MN_LABEL,"Level Subset Settings",0,0);
426   subset_settings_menu->additem(MN_HL,"",0,0);
427   subset_settings_menu->additem(MN_TEXTFIELD,"Title",0,0,MNID_SUBSETTITLE);
428   subset_settings_menu->additem(MN_TEXTFIELD,"Description",0,0,MNID_SUBSETDESCRIPTION);
429   subset_settings_menu->additem(MN_HL,"",0,0);
430   subset_settings_menu->additem(MN_ACTION,"Save Changes",0,0,MNID_SUBSETSAVECHANGES);
431   subset_settings_menu->additem(MN_HL,"",0,0);
432   subset_settings_menu->additem(MN_BACK,"Back",0,0);
433
434   level_settings_menu->arrange_left = true;
435   level_settings_menu->additem(MN_LABEL,"Level Settings",0,0);
436   level_settings_menu->additem(MN_HL,"",0,0);
437   level_settings_menu->additem(MN_TEXTFIELD,   "Name    ",0,0,MNID_NAME);
438   level_settings_menu->additem(MN_TEXTFIELD,   "Author  ",0,0,MNID_AUTHOR);
439   level_settings_menu->additem(MN_STRINGSELECT,"Song    ",0,0,MNID_SONG);
440   level_settings_menu->additem(MN_STRINGSELECT,"Bg-Image",0,0,MNID_BGIMG);
441   level_settings_menu->additem(MN_STRINGSELECT,"Particle",0,0,MNID_PARTICLE);
442   level_settings_menu->additem(MN_NUMFIELD,    "Length  ",0,0,MNID_LENGTH);
443   level_settings_menu->additem(MN_NUMFIELD,    "Height  ",0,0,MNID_HEIGHT);
444   level_settings_menu->additem(MN_NUMFIELD,    "Time    ",0,0,MNID_TIME);
445   level_settings_menu->additem(MN_NUMFIELD,    "Gravity ",0,0,MNID_GRAVITY);
446   level_settings_menu->additem(MN_NUMFIELD,    "Bg-Img-Speed",0,0,MNID_BGSPEED);
447   level_settings_menu->additem(MN_NUMFIELD,    "Top Red     ",0,0,MNID_TopRed);
448   level_settings_menu->additem(MN_NUMFIELD,    "Top Green   ",0,0,MNID_TopGreen);
449   level_settings_menu->additem(MN_NUMFIELD,    "Top Blue    ",0,0,MNID_TopBlue);
450   level_settings_menu->additem(MN_NUMFIELD,    "Bottom Red  ",0,0,MNID_BottomRed);
451   level_settings_menu->additem(MN_NUMFIELD,    "Bottom Green",0,0,MNID_BottomGreen);
452   level_settings_menu->additem(MN_NUMFIELD,    "Bottom Blue ",0,0,MNID_BottomBlue);
453   level_settings_menu->additem(MN_HL,"",0,0);
454   level_settings_menu->additem(MN_ACTION,"Apply Changes",0,0,MNID_APPLY);
455
456   select_tilegroup_menu->arrange_left = true;
457   select_tilegroup_menu->additem(MN_LABEL,"Tilegroup",0,0);
458   select_tilegroup_menu->additem(MN_HL,"",0,0);
459   std::set<TileGroup>* tilegroups = TileManager::tilegroups();
460   int tileid = 1;
461   for(std::set<TileGroup>::iterator it = tilegroups->begin();
462         it != tilegroups->end(); ++it )
463     {
464       select_tilegroup_menu->additem(MN_ACTION, it->name, 0, 0, tileid);
465       tileid++;
466       tilegroups_map[(*it).name] = new ButtonPanel(screen->w - 64,96, 64, 318);
467       i = 0;
468
469       for(std::vector<int>::const_iterator sit = (*it).tiles.begin();
470           sit != (*it).tiles.end(); ++sit, ++i)
471       {
472         std::string imagefile = "/images/tilesets/" ;
473         bool only_editor_image = false;
474         if(!TileManager::instance()->get(*sit)->filenames.empty())
475         {
476           imagefile += TileManager::instance()->get(*sit)->filenames[0];
477         }
478         else if(!TileManager::instance()->get(*sit)->editor_filenames.empty())
479         {
480           imagefile += TileManager::instance()->get(*sit)->editor_filenames[0];
481           only_editor_image = true;
482         }
483         else
484         {
485           imagefile += "notile.png";
486         }
487         Button* button = new Button(imagefile, it->name, SDLKey(SDLK_a + i),
488                                     0, 0, 32, 32);
489         if(!only_editor_image)
490           if(!TileManager::instance()->get(*sit)->editor_filenames.empty())
491           {
492             imagefile = "/images/tilesets/" + TileManager::instance()->get(*sit)->editor_filenames[0];
493             button->add_icon(imagefile,32,32);
494           }
495         tilegroups_map[it->name]->additem(button, *sit);
496       }
497     }
498   select_tilegroup_menu->additem(MN_HL,"",0,0);
499
500   select_objects_menu->arrange_left = true;
501   select_objects_menu->additem(MN_LABEL,"Objects",0,0);
502   select_objects_menu->additem(MN_HL,"",0,0);
503   // TODO fix this
504 #if 0
505   select_objects_menu->additem(MN_ACTION,"BadGuys",0,0,1);
506   objects_map["BadGuys"] = new ButtonPanel(screen->w - 64,96, 64, 318);
507
508   DisplayManager dummy;
509   for(int i = 0; i < NUM_BadGuyKinds; ++i)
510   {
511     BadGuy bad_tmp(dummy, 0,0,BadGuyKind(i),false);
512     objects_map["BadGuys"]->additem(new Button("", "BadGuy",(SDLKey)(i+'a'),0,0,32,32),1000000+i);
513     objects_map["BadGuys"]->manipulate_button(i)->set_drawable(new
514         BadGuy(dummy,
515           objects_map["BadGuys"]->manipulate_button(i)->get_pos().x,
516           objects_map["BadGuys"]->manipulate_button(i)->get_pos().y,
517           BadGuyKind(i), false));
518   }
519 #endif
520
521   select_objects_menu->additem(MN_HL,"",0,0);
522
523 }
524
525 int le_init()
526 {
527
528
529   level_subsets = dsubdirs("/levels", "level1.stl");
530   le_level_subset = new LevelSubset;
531
532   le_world = NULL;
533   selected_game_object = NULL;
534
535   active_tm = TM_IA;
536   le_show_grid = true;
537   show_selections = true;
538   scroll_x = 0;
539
540   done = 0;
541   le_frame = 0; /* support for frames in some tiles, like waves and bad guys */
542   le_level_changed = false;
543   le_help_shown = false;
544
545   le_mouse_pressed[LEFT] = false;
546   le_mouse_pressed[RIGHT] = false;
547
548   le_mouse_clicked[LEFT] = false;
549   le_mouse_clicked[RIGHT] = false;
550
551   le_selection = new Surface(datadir + "/images/leveleditor/select.png", USE_ALPHA);
552
553   select_tilegroup_menu_effect.init(false);
554   select_objects_menu_effect.init(false);
555   display_level_info.init(false);
556
557   /* Load buttons */
558   le_save_level_bt = new Button("/images/icons/save.png","Save level", SDLK_F6,screen->w-64,32);
559   le_exit_bt = new Button("/images/icons/exit.png","Exit", SDLK_F10,screen->w-32,32);
560   le_next_level_bt = new Button("/images/icons/next.png","Next level", SDLK_PAGEUP,screen->w-64,0);
561   le_previous_level_bt = new Button("/images/icons/previous.png","Previous level",SDLK_PAGEDOWN,screen->w-32,0);
562   le_rubber_bt = new Button("/images/icons/rubber.png","Rubber",SDLK_DELETE,screen->w-32,48);
563   le_select_mode_one_bt = new Button ("/images/icons/select-mode1.png","Select single tile",SDLK_F3,screen->w-64,48);
564   le_select_mode_two_bt = new Button("/images/icons/select-mode2.png","Select multiple tiles",SDLK_F3,screen->w-64,48);
565   le_test_level_bt = new Button("/images/icons/test-level.png","Test level",SDLK_F4,screen->w-64,screen->h - 64);
566   le_settings_bt = new Button("/images/icons/settings.png","Level settings",SDLK_F5,screen->w-32,screen->h - 64);
567   le_move_left_bt = new Button("/images/icons/left.png","Move left",SDLK_LEFT,screen->w-80-16,0);
568   le_move_right_bt = new Button("/images/icons/right.png","Move right",SDLK_RIGHT,screen->w-80,0);
569   le_move_up_bt = new Button("/images/icons/up.png","Move up",SDLK_UP,screen->w-80,16);
570   le_move_down_bt = new Button("/images/icons/down.png","Move down",SDLK_DOWN,screen->w-80,32);
571   le_tilegroup_bt = new Button("/images/icons/tilegroup.png","Select Tilegroup", SDLK_F7,screen->w-64,64);
572   le_objects_bt = new Button("/images/icons/objects.png","Select Objects", SDLK_F8,screen->w-64,80);
573   le_object_select_bt = new Button("/images/icons/select-one.png","Select an Object", SDLK_s, screen->w - 64, screen->h-98);
574   le_object_properties_bt = new Button("/images/icons/properties.png","Edit object properties", SDLK_p, screen->w - 32, screen->h-98);
575   le_object_properties_bt->set_active(false);
576
577   mouse_select_object = new MouseCursor(datadir + "/images/status/select-cursor.png",1);
578   mouse_select_object->set_mid(16,16);
579
580   le_tilemap_panel = new ButtonPanel(screen->w-64,screen->h-32,32,32);
581   le_tilemap_panel->set_button_size(32,10);
582   le_tilemap_panel->additem(new Button("/images/icons/bkgrd.png","Background",SDLK_b,0,0),TM_BG);
583   le_tilemap_panel->additem(new Button("/images/icons/intact.png","Interactive",SDLK_i,0,0),TM_IA);
584   le_tilemap_panel->additem(new Button("/images/icons/frgrd.png","Foreground",SDLK_f,0,0),TM_FG);
585   le_tilemap_panel->highlight_last(true);
586   le_tilemap_panel->set_last_clicked(TM_IA);
587
588   le_current.Init();
589
590   le_init_menus();
591
592   SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
593
594
595   return 0;
596 }
597
598 void update_level_settings_menu()
599 {
600   char str[80];
601   int i;
602
603   level_settings_menu->get_item_by_id(MNID_NAME).change_input(le_world->get_level()->name.c_str());
604   level_settings_menu->get_item_by_id(MNID_AUTHOR).change_input(le_world->get_level()->author.c_str());
605
606   string_list_copy(level_settings_menu->get_item_by_id(MNID_SONG).list, dfiles("music/",NULL, "-fast"));
607   string_list_copy(level_settings_menu->get_item_by_id(MNID_BGIMG).list, dfiles("images/background",NULL, NULL));
608   string_list_add_item(level_settings_menu->get_item_by_id(MNID_BGIMG).list,"");
609   string_list_add_item(level_settings_menu->get_item_by_id(MNID_PARTICLE).list,"");
610   string_list_add_item(level_settings_menu->get_item_by_id(MNID_PARTICLE).list,"snow");
611   string_list_add_item(level_settings_menu->get_item_by_id(MNID_PARTICLE).list,"clouds");
612
613   if((i = string_list_find(level_settings_menu->get_item_by_id(MNID_SONG).list,le_world->get_level()->song_title.c_str())) != -1)
614     level_settings_menu->get_item_by_id(MNID_SONG).list->active_item = i;
615   if((i = string_list_find(level_settings_menu->get_item_by_id(MNID_BGIMG).list,le_world->get_level()->bkgd_image.c_str())) != -1)
616     level_settings_menu->get_item_by_id(MNID_BGIMG).list->active_item = i;
617   if((i = string_list_find(level_settings_menu->get_item_by_id(MNID_PARTICLE).list,le_world->get_level()->particle_system.c_str())) != -1)
618     level_settings_menu->get_item_by_id(MNID_PARTICLE).list->active_item = i;
619
620   sprintf(str,"%d",le_world->get_level()->width);
621   level_settings_menu->get_item_by_id(MNID_LENGTH).change_input(str);
622   sprintf(str,"%d",le_world->get_level()->height);
623   level_settings_menu->get_item_by_id(MNID_HEIGHT).change_input(str);
624   sprintf(str,"%d",le_world->get_level()->time_left);
625   level_settings_menu->get_item_by_id(MNID_TIME).change_input(str);
626   sprintf(str,"%2.0f",le_world->get_level()->gravity);
627   level_settings_menu->get_item_by_id(MNID_GRAVITY).change_input(str);
628   sprintf(str,"%d",le_world->get_level()->bkgd_speed);
629   level_settings_menu->get_item_by_id(MNID_BGSPEED).change_input(str);
630   sprintf(str,"%d",le_world->get_level()->bkgd_top.red);
631   level_settings_menu->get_item_by_id(MNID_TopRed).change_input(str);
632   sprintf(str,"%d",le_world->get_level()->bkgd_top.green);
633   level_settings_menu->get_item_by_id(MNID_TopGreen).change_input(str);
634   sprintf(str,"%d",le_world->get_level()->bkgd_top.blue);
635   level_settings_menu->get_item_by_id(MNID_TopBlue).change_input(str);
636   sprintf(str,"%d",le_world->get_level()->bkgd_bottom.red);
637   level_settings_menu->get_item_by_id(MNID_BottomRed).change_input(str);
638   sprintf(str,"%d",le_world->get_level()->bkgd_bottom.green);
639   level_settings_menu->get_item_by_id(MNID_BottomGreen).change_input(str);
640   sprintf(str,"%d",le_world->get_level()->bkgd_bottom.blue);
641   level_settings_menu->get_item_by_id(MNID_BottomBlue).change_input(str);
642 }
643
644 void update_subset_settings_menu()
645 {
646   subset_settings_menu->item[2].change_input(le_level_subset->title.c_str());
647   subset_settings_menu->item[3].change_input(le_level_subset->description.c_str());
648 }
649
650 void apply_level_settings_menu()
651 {
652   int i;
653   i = false;
654   le_level_changed = true;
655
656   le_world->get_level()->name = level_settings_menu->get_item_by_id(MNID_NAME).input;
657   le_world->get_level()->author = level_settings_menu->get_item_by_id(MNID_AUTHOR).input;
658
659   if(le_world->get_level()->bkgd_image.compare(string_list_active(level_settings_menu->get_item_by_id(MNID_BGIMG).list)) != 0)
660   {
661     le_world->get_level()->bkgd_image = string_list_active(level_settings_menu->get_item_by_id(MNID_BGIMG).list);
662     i = true;
663   }
664
665   if(le_world->get_level()->particle_system.compare(string_list_active(level_settings_menu->get_item_by_id(MNID_PARTICLE).list)) != 0)
666   {
667     le_world->get_level()->particle_system = string_list_active(level_settings_menu->get_item_by_id(MNID_PARTICLE).list);
668   }
669
670   if(i)
671   {
672     le_world->get_level()->load_gfx();
673   }
674
675   le_world->get_level()->song_title = string_list_active(level_settings_menu->get_item_by_id(MNID_SONG).list);
676
677   le_world->get_level()->resize(
678       atoi(level_settings_menu->get_item_by_id(MNID_LENGTH).input),
679       atoi(level_settings_menu->get_item_by_id(MNID_HEIGHT).input));
680   le_world->get_level()->time_left = atoi(level_settings_menu->get_item_by_id(MNID_TIME).input);
681   le_world->get_level()->gravity = atof(level_settings_menu->get_item_by_id(MNID_GRAVITY).input);
682   le_world->get_level()->bkgd_speed = atoi(level_settings_menu->get_item_by_id(MNID_BGSPEED).input);
683   le_world->get_level()->bkgd_top.red = atoi(level_settings_menu->get_item_by_id(MNID_TopRed).input);
684   le_world->get_level()->bkgd_top.green = atoi(level_settings_menu->get_item_by_id(MNID_TopGreen).input);
685   le_world->get_level()->bkgd_top.blue = atoi(level_settings_menu->get_item_by_id(MNID_TopBlue).input);
686   le_world->get_level()->bkgd_bottom.red = atoi(level_settings_menu->get_item_by_id(MNID_BottomRed).input);
687   le_world->get_level()->bkgd_bottom.green = atoi(level_settings_menu->get_item_by_id(MNID_BottomGreen).input);
688   le_world->get_level()->bkgd_bottom.blue = atoi(level_settings_menu->get_item_by_id(MNID_BottomBlue).input);
689 }
690
691 void save_subset_settings_menu()
692 {
693   le_level_subset->title = subset_settings_menu->item[2].input;
694   le_level_subset->description = subset_settings_menu->item[3].input;
695   le_level_subset->save();
696   le_level_changed = false;
697 }
698
699 void le_unload_level()
700 {
701   if(le_level_changed)
702   {
703     le_drawlevel();
704     le_drawinterface();
705     char str[1024];
706     sprintf(str,"Save changes to level %d of %s?",le_level,le_level_subset->name.c_str());
707     if(confirm_dialog(str))
708     {
709       le_world->get_level()->save(le_level_subset->name.c_str(), le_level,
710           le_world);
711     }
712   }
713
714   delete le_world;
715   le_level_changed = false;
716 }
717
718 void le_goto_level(int levelnb)
719 {
720   le_unload_level();
721   le_world = new World(le_level_subset->name, levelnb);
722   display_level_info.start(2500);
723   le_level = levelnb;
724 }
725
726 void le_quit(void)
727 {
728   SDL_EnableKeyRepeat(0, 0);    // disables key repeating
729
730   le_unload_level();
731   delete le_selection;
732   delete leveleditor_menu;
733   delete subset_load_menu;
734   delete subset_new_menu;
735   delete subset_settings_menu;
736   delete level_settings_menu;
737   delete select_tilegroup_menu;
738   delete select_objects_menu;
739   delete le_save_level_bt;
740   delete le_exit_bt;
741   delete le_test_level_bt;
742   delete le_next_level_bt;
743   delete le_previous_level_bt;
744   delete le_move_right_bt;
745   delete le_move_left_bt;
746   delete le_move_up_bt;
747   delete le_move_down_bt;
748   delete le_rubber_bt;
749   delete le_select_mode_one_bt;
750   delete le_select_mode_two_bt;
751   delete le_settings_bt;
752   delete le_tilegroup_bt;
753   delete le_objects_bt;
754   delete le_tilemap_panel;
755   delete le_object_select_bt;
756   delete le_object_properties_bt;
757   delete mouse_select_object;
758
759   delete le_level_subset;
760   le_level_subset = 0;
761
762   for(ButtonPanelMap::iterator i = tilegroups_map.begin();
763       i != tilegroups_map.end(); ++i)
764   {
765     delete i->second;
766   }
767   for(ButtonPanelMap::iterator i = objects_map.begin();
768       i != objects_map.end(); ++i)
769   {
770     delete i->second;
771   }
772 }
773
774 void le_drawminimap()
775 {
776   if(le_world == NULL)
777     return;
778
779   int mini_tile_width;
780   if(screen->w - 64 > le_world->get_level()->width * 4)
781     mini_tile_width = 4;
782   else if(screen->w - 64 > le_world->get_level()->width * 2)
783     mini_tile_width = 2;
784   else
785     mini_tile_width = 1;
786   int left_offset = (screen->w - 64 - le_world->get_level()->width*mini_tile_width) / 2;
787
788   int mini_tile_height;
789   if(screen->h - 64 > le_world->get_level()->height * 4)
790     mini_tile_height = 4;
791   else if(screen->h - 64 > le_world->get_level()->height * 2)
792     mini_tile_height = 2;
793   else
794     mini_tile_height = 1;
795
796   Level* level = le_world->get_level();
797   for (int y = 0; y < le_world->get_level()->height; ++y)
798     for (int x = 0; x < le_world->get_level()->width; ++x)
799     {
800
801       Tile::draw_stretched(left_offset + mini_tile_width*x, y * 4,
802           mini_tile_width , 4, level->bg_tiles[y * level->width + x]);
803
804       Tile::draw_stretched(left_offset + mini_tile_width*x, y * 4,
805           mini_tile_width , 4, level->ia_tiles[y * level->width + x]);
806
807       Tile::draw_stretched(left_offset + mini_tile_width*x, y * 4,
808           mini_tile_width , 4, level->fg_tiles[y + level->width + x]);
809
810     }
811
812   fillrect(left_offset, 0, le_world->get_level()->width*mini_tile_width, le_world->get_level()->height*mini_tile_height, 200, 200, 200, 128);
813
814   fillrect(left_offset + (pos_x/32)*mini_tile_width, 0, 19*mini_tile_width, 2, 200, 200, 200, 200);
815   fillrect(left_offset + (pos_x/32)*mini_tile_width, 0, 2, le_world->get_level()->height*mini_tile_height, 200, 200, 200, 200);
816   fillrect(left_offset + (pos_x/32)*mini_tile_width + 19*mini_tile_width - 2, 0, 2, le_world->get_level()->height*mini_tile_height, 200, 200, 200, 200);
817   fillrect(left_offset + (pos_x/32)*mini_tile_width, le_world->get_level()->height*mini_tile_height-2, 19*mini_tile_width, 2, 200, 200, 200, 200);
818
819 }
820
821 void le_drawinterface()
822 {
823   int x,y;
824   char str[80];
825
826   if(le_world != NULL)
827   {
828     /* draw a grid (if selected) */
829     if(le_show_grid)
830     {
831       for(x = 0; x < VISIBLE_TILES_X; x++)
832         fillrect(x*32 - ((int)pos_x % 32), 0, 1, screen->h, 225, 225, 225,255);
833       for(y = 0; y < VISIBLE_TILES_Y; y++)
834         fillrect(0, y*32 - ((int)pos_y % 32), screen->w, 1, 225, 225, 225,255);
835     }
836   }
837
838   if(show_minimap) // use_gl because the minimap isn't shown correctly in software mode. Any idea? FIXME Possible reasons: SDL_SoftStretch is a hack itsself || an alpha blitting issue SDL can't handle in software mode
839     le_drawminimap();
840
841   if(show_selections && MouseCursor::current() != mouse_select_object)
842   {
843     if(le_selection_mode == CURSOR)
844     {
845       if(le_current.IsTile())
846         le_selection->draw(cursor_x - pos_x, cursor_y - pos_y);
847     }
848     else if(le_selection_mode == SQUARE)
849     {
850       int w, h;
851       le_highlight_selection();
852       /* draw current selection */
853       w = selection.x2 - selection.x1;
854       h = selection.y2 - selection.y1;
855       fillrect(selection.x1 - pos_x, selection.y1 - pos_y, w, SELECT_W, SELECT_CLR);
856       fillrect(selection.x1 - pos_x + w, selection.y1 - pos_y, SELECT_W, h, SELECT_CLR);
857       fillrect(selection.x1 - pos_x, selection.y1 - pos_y + h, w, SELECT_W, SELECT_CLR);
858       fillrect(selection.x1 - pos_x, selection.y1 - pos_y, SELECT_W, h, SELECT_CLR);
859     }
860   }
861
862
863   /* draw button bar */
864   fillrect(screen->w - 64, 0, 64, screen->h, 50, 50, 50,255);
865
866   if(le_current.IsTile())
867   {
868     Tile::draw(screen->w - 32, screen->h - 32, le_current.tile);
869     if(TileManager::instance()->get(le_current.tile)->editor_images.size() > 0)
870       TileManager::instance()->get(le_current.tile)->editor_images[0]->draw( screen->w - 32, screen->h - 32);
871   }
872 #if 0 // XXX FIXME TODO
873   if(le_current.IsObject() && MouseCursor::current() != mouse_select_object)
874   {
875     le_current.obj->draw_on_screen(screen->w - 32, screen->h - 32);
876     le_current.obj->draw_on_screen(cursor_x,cursor_y);
877   }
878 #endif
879
880   if(mouse_select_object && selected_game_object != NULL)
881   {
882     fillrect(selected_game_object->base.x-pos_x,selected_game_object->base.y-pos_y,selected_game_object->base.width,3,255,0,0,255);
883     fillrect(selected_game_object->base.x-pos_x,selected_game_object->base.y-pos_y,3,selected_game_object->base.height,255,0,0,255);
884     fillrect(selected_game_object->base.x-pos_x,selected_game_object->base.y-pos_y+selected_game_object->base.height,selected_game_object->base.width,3,255,0,0,255);
885     fillrect(selected_game_object->base.x-pos_x+selected_game_object->base.width,selected_game_object->base.y-pos_y,3,selected_game_object->base.height,255,0,0,255);
886   }
887
888   if(le_world != NULL)
889   {
890     le_save_level_bt->draw();
891     le_exit_bt->draw();
892     le_test_level_bt->draw();
893     le_next_level_bt->draw();
894     le_previous_level_bt->draw();
895     le_rubber_bt->draw();
896     if(le_selection_mode == SQUARE)
897       le_select_mode_one_bt->draw();
898     else if(le_selection_mode == CURSOR)
899       le_select_mode_two_bt->draw();
900     le_settings_bt->draw();
901     le_move_right_bt->draw();
902     le_move_left_bt->draw();
903     le_move_up_bt->draw();
904     le_move_down_bt->draw();
905     le_tilegroup_bt->draw();
906     le_objects_bt->draw();
907     if(!cur_tilegroup.empty())
908       tilegroups_map[cur_tilegroup]->draw();
909     else if(!cur_objects.empty())
910     {
911       objects_map[cur_objects]->draw();
912     }
913
914     le_tilemap_panel->draw();
915
916     if(!cur_objects.empty())
917     {
918       le_object_select_bt->draw();
919       le_object_properties_bt->draw();
920     }
921
922     sprintf(str, "%d/%d", le_level,le_level_subset->levels);
923     white_text->drawf(str, (le_level_subset->levels < 10) ? -10 : 0, 16, A_RIGHT, A_TOP, 0);
924
925     if(!le_help_shown)
926       white_small_text->draw("F1 for Help", 10, 430, 1);
927
928     if(display_level_info.check())
929       white_text->drawf(le_world->get_level()->name.c_str(), 0, 0, A_HMIDDLE, A_TOP, 0);
930   }
931   else
932   {
933     if(!Menu::current())
934       white_small_text->draw("No Level Subset loaded - Press ESC and choose one in the menu", 10, 430, 1);
935     else
936       white_small_text->draw("No Level Subset loaded", 10, 430, 1);
937   }
938
939 }
940
941 void le_drawlevel()
942 {
943   unsigned int y,x;
944   Uint8 a;
945
946   /* Draw the real background */
947   le_world->get_level()->draw_bg();
948
949   if(le_current.IsTile())
950   {
951     Tile::draw(cursor_x-pos_x, cursor_y-pos_y,le_current.tile,128);
952     if(!TileManager::instance()->get(le_current.tile)->images.empty())
953       fillrect(cursor_x-pos_x,cursor_y-pos_y,TileManager::instance()->get(le_current.tile)->images[0]->w,TileManager::instance()->get(le_current.tile)->images[0]->h,50,50,50,50);
954   }
955 #if 0 // XXX FIXME TODO
956   if(le_current.IsObject())
957   {
958     le_current.obj->move_to(cursor_x, cursor_y);
959   }
960 #endif
961
962   /*       clearscreen(current_level.bkgd_red, current_level.bkgd_green, current_level.bkgd_blue); */
963
964   Level* level = le_world->get_level();
965   for (y = 0; y < VISIBLE_TILES_Y && y < (unsigned)le_world->get_level()->height; ++y)
966     for (x = 0; x < (unsigned)VISIBLE_TILES_X - 2; ++x)
967     {
968
969       if(active_tm == TM_BG)
970         a = 255;
971       else
972         a = 128;
973
974       Tile::draw(32*x - fmodf(pos_x, 32), y*32 - fmodf(pos_y, 32),
975           level->bg_tiles[ (y + (int)(pos_y / 32)) * level->width + 
976           (x + (int)(pos_x / 32))],a);
977
978       if(active_tm == TM_IA)
979         a = 255;
980       else
981         a = 128;
982
983       Tile::draw(32*x - fmodf(pos_x, 32), y*32 - fmodf(pos_y, 32),
984           level->ia_tiles[ (y + (int)(pos_y / 32)) * level->width +       
985           (x + (int)(pos_x / 32))],a);
986
987       
988       if(active_tm == TM_FG)
989         a = 255;
990       else
991         a = 128;
992
993       Tile::draw(32*x - fmodf(pos_x, 32), y*32 - fmodf(pos_y, 32),
994           level->fg_tiles[ (y + (int)(pos_y / 32)) * level->width +       
995           (x + (int)(pos_x / 32))],a);
996
997       /* draw whats inside stuff when cursor is selecting those */
998       /* (draw them all the time - is this the right behaviour?) */
999       Tile* edit_image = TileManager::instance()->get(
1000           level->ia_tiles
1001           [ (y + (int)(pos_y / 32)) * level->width + (x + (int)(pos_x / 32))]);
1002       if(edit_image && !edit_image->editor_images.empty())
1003         edit_image->editor_images[0]->draw( x * 32 - ((int)pos_x % 32), y*32 - ((int)pos_y % 32));
1004
1005     }
1006
1007   /* Draw the Bad guys: */
1008   for (std::vector<GameObject*>::iterator it = le_world->gameobjects.begin();
1009        it != le_world->gameobjects.end(); ++it)
1010   {
1011     BadGuy* badguy = dynamic_cast<BadGuy*> (*it);
1012     if(badguy == 0)
1013       continue;
1014     
1015     /* to support frames: img_bsod_left[(frame / 5) % 4] */
1016     ViewPort viewport;
1017     viewport.set_translation(Vector(pos_x, pos_y));
1018     badguy->draw(viewport, 0);
1019   }
1020
1021   /* Draw the player: */
1022   /* for now, the position is fixed at (100, 240) */
1023   largetux.walk_right->draw( 100 - pos_x, 240 - pos_y);
1024 }
1025
1026 void le_change_object_properties(GameObject *pobj)
1027 {
1028   Surface* cap_screen = Surface::CaptureScreen();
1029   Menu* object_properties_menu = new Menu();
1030   bool loop = true;
1031
1032   object_properties_menu->additem(MN_LABEL,pobj->type() + " Properties",0,0);
1033   object_properties_menu->additem(MN_HL,"",0,0);
1034
1035   BadGuy* pbad = dynamic_cast<BadGuy*>(pobj);
1036   if(pobj != 0)
1037   {
1038     object_properties_menu->additem(MN_STRINGSELECT,"Kind",0,0,1);
1039     for(int i = 0; i < NUM_BadGuyKinds; ++i)
1040     {
1041       string_list_add_item(object_properties_menu->get_item_by_id(1).list,
1042           badguykind_to_string(static_cast<BadGuyKind>(i)).c_str());
1043       if(pbad->kind == i)
1044         object_properties_menu->get_item_by_id(1).list->active_item = i;
1045     }
1046     object_properties_menu->additem(MN_TOGGLE,"StayOnPlatform",pbad->stay_on_platform,0,2);
1047   }
1048
1049   object_properties_menu->additem(MN_HL,"",0,0);
1050   object_properties_menu->additem(MN_ACTION,"Ok",0,0,3);
1051
1052   Menu::set_current(object_properties_menu);
1053
1054   while(loop)
1055   {
1056     SDL_Event event;
1057
1058     while (SDL_PollEvent(&event))
1059     {
1060       object_properties_menu->event(event);
1061     }
1062
1063     cap_screen->draw(0,0);
1064
1065     object_properties_menu->draw();
1066     object_properties_menu->action();
1067
1068     switch (object_properties_menu->check())
1069     {
1070     case 3:
1071       {
1072       BadGuy* pbad = dynamic_cast<BadGuy*>(pobj);
1073       if(pbad != 0) {
1074         BadGuy* pbad = dynamic_cast<BadGuy*>(pobj);
1075         pbad->kind =  badguykind_from_string(string_list_active(object_properties_menu->get_item_by_id(1).list));
1076         pbad->stay_on_platform = object_properties_menu->get_item_by_id(2).toggled;
1077       }
1078       loop = false;
1079       break;
1080       }
1081     default:
1082       break;
1083     }
1084
1085     if(Menu::current() == NULL)
1086       loop = false;
1087
1088     mouse_cursor->draw();
1089     flipscreen();
1090     SDL_Delay(25);
1091   }
1092
1093   delete cap_screen;
1094   Menu::set_current(0);
1095   delete object_properties_menu;
1096 }
1097
1098
1099 void le_checkevents()
1100 {
1101   SDLKey key;
1102   SDLMod keymod;
1103   Button* pbutton;
1104   int x,y;
1105
1106   keymod = SDL_GetModState();
1107
1108   while(SDL_PollEvent(&event))
1109   {
1110     if (Menu::current())
1111     {
1112       Menu::current()->event(event);
1113       if(!le_world && !Menu::current())
1114         Menu::set_current(leveleditor_menu);
1115     }
1116     else
1117     {
1118       mouse_cursor->set_state(MC_NORMAL);
1119
1120       /* testing SDL_KEYDOWN, SDL_KEYUP and SDL_QUIT events*/
1121       if(event.type == SDL_KEYDOWN
1122           || ((event.type == SDL_MOUSEBUTTONDOWN || SDL_MOUSEMOTION)
1123               && (event.motion.x > 0
1124                   && event.motion.x < screen->w - 64 &&
1125                   event.motion.y > 0 && event.motion.y < screen->h)))
1126       {
1127         switch(event.type)
1128         {
1129         case SDL_KEYDOWN:       // key pressed
1130           key = event.key.keysym.sym;
1131           switch(key)
1132           {
1133           case SDLK_ESCAPE:
1134             Menu::set_current(leveleditor_menu);
1135             break;
1136           case SDLK_F1:
1137             if(le_world != NULL)
1138               le_showhelp();
1139             break;
1140           case SDLK_HOME:
1141             cursor_x = 0;
1142             pos_x = cursor_x;
1143             break;
1144           case SDLK_END:
1145             cursor_x = (le_world->get_level()->width * 32) - 32;
1146             pos_x = cursor_x;
1147             break;
1148           case SDLK_F9:
1149             le_show_grid = !le_show_grid;
1150             break;
1151           default:
1152             break;
1153           }
1154           break;
1155         case SDL_MOUSEBUTTONDOWN:
1156           if(event.button.button == SDL_BUTTON_LEFT)
1157           {
1158             le_mouse_pressed[LEFT] = true;
1159
1160             selection.x1 = event.motion.x + pos_x;
1161             selection.y1 = event.motion.y + pos_y;
1162             selection.x2 = event.motion.x + pos_x;
1163             selection.y2 = event.motion.y + pos_y;
1164           }
1165           else if(event.button.button == SDL_BUTTON_RIGHT)
1166           {
1167             le_mouse_pressed[RIGHT] = true;
1168           }
1169           break;
1170         case SDL_MOUSEBUTTONUP:
1171           if(event.button.button == SDL_BUTTON_LEFT)
1172           {
1173             le_mouse_pressed[LEFT] = false;
1174             le_mouse_clicked[LEFT] = true;
1175           }
1176           else if(event.button.button == SDL_BUTTON_RIGHT)
1177           {
1178             le_mouse_pressed[RIGHT] = false;
1179             le_mouse_clicked[RIGHT] = true;
1180           }
1181           break;
1182         case SDL_MOUSEMOTION:
1183
1184           if(!Menu::current())
1185           {
1186             x = event.motion.x;
1187             y = event.motion.y;
1188
1189             if(le_current.IsTile())
1190             {
1191               cursor_x = ((int)(pos_x + x) / 32) * 32;
1192               cursor_y = ((int)(pos_y + y) / 32) * 32;
1193             }
1194             else
1195             {
1196               cursor_x = x;
1197               cursor_y = y;
1198             }
1199
1200             if(le_mouse_pressed[LEFT])
1201             {
1202               selection.x2 = x + pos_x;
1203               selection.y2 = y + pos_y;
1204             }
1205
1206             if(le_mouse_pressed[RIGHT])
1207             {
1208               pos_x += -1 * event.motion.xrel;
1209               pos_y += -1 * event.motion.yrel;
1210             }
1211           }
1212           break;
1213         default:
1214           break;
1215         }
1216       }
1217       else if(event.type == SDL_QUIT) /* window closing */
1218       {
1219       done = 1;
1220       }
1221     }
1222
1223     if(le_world != NULL)
1224     {
1225       if(event.type == SDL_KEYDOWN || event.type == SDL_KEYUP || ((event.type == SDL_MOUSEBUTTONDOWN || SDL_MOUSEMOTION) && (event.motion.x > screen->w-64 && event.motion.x < screen->w &&
1226           event.motion.y > 0 && event.motion.y < screen->h)))
1227       {
1228         le_mouse_pressed[LEFT] = false;
1229         le_mouse_pressed[RIGHT] = false;
1230
1231         if(!Menu::current())
1232         {
1233           /* Check for button events */
1234           le_test_level_bt->event(event);
1235           if(le_test_level_bt->get_state() == BUTTON_CLICKED)
1236             le_testlevel();
1237           le_save_level_bt->event(event);
1238           if(le_save_level_bt->get_state() == BUTTON_CLICKED)
1239             le_world->get_level()->save(le_level_subset->name.c_str(),le_level,
1240                 le_world);
1241           le_exit_bt->event(event);
1242           if(le_exit_bt->get_state() == BUTTON_CLICKED)
1243           {
1244             Menu::set_current(leveleditor_menu);
1245           }
1246           le_next_level_bt->event(event);
1247           if(le_next_level_bt->get_state() == BUTTON_CLICKED)
1248           {
1249             if(le_level < le_level_subset->levels)
1250             {
1251               le_goto_level(le_level+1);
1252             }
1253             else
1254             {
1255               Level new_lev;
1256               char str[1024];
1257               sprintf(str,"Level %d doesn't exist. Create it?",le_level+1);
1258               if(confirm_dialog(str))
1259               {
1260                 new_lev.init_defaults();
1261                 new_lev.save(le_level_subset->name.c_str(),le_level+1, le_world);
1262                 le_level_subset->levels = le_level;
1263                 le_goto_level(le_level);
1264               }
1265             }
1266           }
1267           le_previous_level_bt->event(event);
1268           if(le_previous_level_bt->get_state() == BUTTON_CLICKED)
1269           {
1270             if(le_level > 1)
1271               le_goto_level(le_level -1);
1272           }
1273           le_rubber_bt->event(event);
1274           if(le_rubber_bt->get_state() == BUTTON_CLICKED)
1275             le_current.Tile(0);
1276
1277           if(!cur_objects.empty())
1278           {
1279             le_object_select_bt->event(event);
1280             if(le_object_select_bt->get_state() == BUTTON_CLICKED)
1281             {
1282               MouseCursor::set_current(mouse_select_object);
1283             }
1284
1285             le_object_properties_bt->event(event);
1286             if(le_object_properties_bt->get_state() == BUTTON_CLICKED)
1287             {
1288               le_change_object_properties(selected_game_object);
1289             }
1290           }
1291
1292
1293           if(le_selection_mode == SQUARE)
1294           {
1295             le_select_mode_one_bt->event(event);
1296             if(le_select_mode_one_bt->get_state() == BUTTON_CLICKED)
1297               le_selection_mode = CURSOR;
1298           }
1299           else
1300           {
1301             le_select_mode_two_bt->event(event);
1302             if(le_select_mode_two_bt->get_state() == BUTTON_CLICKED)
1303               le_selection_mode = SQUARE;
1304           }
1305           ButtonPanelMap::iterator it;
1306           le_tilegroup_bt->event(event);
1307           switch (le_tilegroup_bt->get_state())
1308           {
1309           case BUTTON_CLICKED:
1310             Menu::set_current(select_tilegroup_menu);
1311             select_tilegroup_menu_effect.start(200);
1312             select_tilegroup_menu->set_pos(screen->w - 64,100,-0.5,0.5);
1313             break;
1314           case BUTTON_WHEELUP:
1315             if(cur_tilegroup.empty())
1316             {
1317               cur_tilegroup = tilegroups_map.begin()->first;
1318             }
1319             else
1320             {
1321               it = tilegroups_map.find(cur_tilegroup);
1322               if((++it) == tilegroups_map.end())
1323               {
1324                 cur_tilegroup = tilegroups_map.begin()->first;
1325               }
1326               else
1327               {
1328                 cur_tilegroup = (*it).first;
1329               }
1330             }
1331
1332             cur_objects = "";
1333             break;
1334           case BUTTON_WHEELDOWN:
1335             it = tilegroups_map.find(cur_tilegroup);
1336             if(it == tilegroups_map.begin())
1337             {
1338               cur_tilegroup = tilegroups_map.rbegin()->first;
1339               cur_objects = "";
1340               break;
1341             }
1342             if(--it != --tilegroups_map.begin())
1343               cur_tilegroup = (*it).first;
1344             else
1345               cur_tilegroup = tilegroups_map.rbegin()->first;
1346
1347             cur_objects = "";
1348             break;
1349           default:
1350             break;
1351           }
1352
1353           le_objects_bt->event(event);
1354           switch (le_objects_bt->get_state())
1355           {
1356           case BUTTON_CLICKED:
1357             Menu::set_current(select_objects_menu);
1358             select_objects_menu_effect.start(200);
1359             select_objects_menu->set_pos(screen->w - 64,100,-0.5,0.5);
1360             break;
1361           case BUTTON_WHEELUP:
1362             it = objects_map.find(cur_objects);
1363             if(it == objects_map.end())
1364             {
1365               cur_objects = objects_map.begin()->first;
1366               cur_tilegroup = "";
1367               break;
1368             }
1369             if(++it != objects_map.end())
1370               cur_objects = (*it).first;
1371             else
1372               cur_objects = objects_map.begin()->first;
1373
1374             cur_tilegroup = "";
1375             break;
1376           case BUTTON_WHEELDOWN:
1377             it = objects_map.find(cur_objects);
1378             if(it == objects_map.begin())
1379             {
1380               cur_objects = objects_map.rbegin()->first;
1381               cur_tilegroup = "";
1382               break;
1383             }
1384             if(--it != --objects_map.begin())
1385               cur_objects = (*it).first;
1386             else
1387               cur_objects = objects_map.rbegin()->first;
1388
1389             cur_tilegroup = "";
1390             break;
1391             break;
1392           default:
1393             break;
1394           }
1395
1396           le_settings_bt->event(event);
1397           if(le_settings_bt->get_state() == BUTTON_CLICKED)
1398           {
1399             update_level_settings_menu();
1400             Menu::set_current(level_settings_menu);
1401           }
1402           if(!cur_tilegroup.empty())
1403           {
1404             if((pbutton = tilegroups_map[cur_tilegroup]->event(event)) != NULL)
1405             {
1406               if(pbutton->get_state() == BUTTON_CLICKED)
1407               {
1408                 le_current.Tile(pbutton->get_tag());
1409               }
1410             }
1411           }
1412           else if(!cur_objects.empty())
1413           {
1414             if((pbutton = objects_map[cur_objects]->event(event)) != NULL)
1415             {
1416               if(pbutton->get_state() == BUTTON_CLICKED)
1417               {
1418 #if 0   // TODO fixme!!
1419                 le_current.Object(pbutton->get_drawable());
1420 #endif
1421               }
1422             }
1423           }
1424
1425           if((pbutton = le_tilemap_panel->event(event)) != NULL)
1426           {
1427             if(pbutton->get_state() == BUTTON_CLICKED)
1428             {
1429               active_tm = static_cast<TileMapType>(pbutton->get_tag());
1430             }
1431           }
1432         }
1433         else
1434         {
1435           le_settings_bt->event(event);
1436           if(le_settings_bt->get_state() == BUTTON_CLICKED)
1437           {
1438             Menu::set_current(0);
1439           }
1440           le_tilegroup_bt->event(event);
1441           if(le_tilegroup_bt->get_state() == BUTTON_CLICKED)
1442           {
1443             Menu::set_current(0);
1444           }
1445           le_objects_bt->event(event);
1446           if(le_objects_bt->get_state() == BUTTON_CLICKED)
1447           {
1448             Menu::set_current(0);
1449           }
1450         }
1451       }
1452
1453       if(!Menu::current() && !show_minimap)
1454       {
1455         if(le_mouse_pressed[LEFT])
1456         {
1457           if(MouseCursor::current() != mouse_select_object)
1458           {
1459             if(le_current.IsTile())
1460               le_change(cursor_x, cursor_y, active_tm, le_current.tile);
1461           }
1462         }
1463         else if(le_mouse_clicked[LEFT])
1464         {
1465           if(MouseCursor::current() == mouse_select_object)
1466           {
1467             bool object_got_hit = false;
1468             base_type cursor_base;
1469             if(le_current.IsTile())
1470             {
1471             cursor_base.x = cursor_x;
1472             cursor_base.y = cursor_y;
1473             }
1474             else if(le_current.IsObject())
1475             {
1476             cursor_base.x = cursor_x + pos_x;
1477             cursor_base.y = cursor_y + pos_y;       
1478             }
1479             cursor_base.width = 32;
1480             cursor_base.height = 32;
1481
1482             for(std::vector<GameObject*>::iterator it =
1483                 le_world->gameobjects.begin();
1484                 it != le_world->gameobjects.end(); ++it) {
1485               MovingObject* mobj = dynamic_cast<MovingObject*> (*it);
1486               if(!mobj)
1487                 continue;
1488
1489               if(rectcollision(cursor_base, mobj->base))
1490               {
1491                 selected_game_object = mobj;
1492                 object_got_hit = true;
1493                 break;
1494               }
1495             }
1496
1497             if(!object_got_hit)
1498             {
1499               selected_game_object = NULL;
1500               le_object_properties_bt->set_active(false);
1501             }
1502             else
1503               le_object_properties_bt->set_active(true);
1504
1505             MouseCursor::set_current(mouse_cursor);
1506
1507           }
1508           else
1509           {
1510 #if 0 // FIXME TODO
1511             if(le_current.IsObject())
1512             {
1513               le_level_changed  = true;
1514               std::string type = le_current.obj->type();
1515               if(type == "BadGuy")
1516               {
1517                 BadGuy* pbadguy = dynamic_cast<BadGuy*>(le_current.obj);
1518
1519                 le_world->bad_guys.push_back(new BadGuy(cursor_x+scroll_x, cursor_y,pbadguy->kind,false));
1520                 le_world->get_level()->badguy_data.push_back(le_world->bad_guys.back());
1521               }
1522             }
1523 #endif
1524           }
1525           
1526           le_mouse_clicked[LEFT] = false;
1527
1528         }
1529       }
1530     }
1531   }
1532   if(!Menu::current())
1533   {
1534     show_minimap = false;
1535
1536     le_move_left_bt->event(event);
1537     le_move_right_bt->event(event);
1538     le_move_up_bt->event(event);
1539     le_move_down_bt->event(event);
1540     switch(le_move_left_bt->get_state())
1541     {
1542     case BUTTON_PRESSED:
1543       pos_x -= KEYBOARD_SPEED;
1544       show_minimap = true;
1545       break;
1546     case BUTTON_HOVER:
1547       pos_x -= MOUSE_SPEED;
1548       show_minimap = true;
1549       break;
1550     case BUTTON_CLICKED:
1551       show_minimap = true;
1552       break;
1553     default:
1554       break;
1555     }
1556
1557     switch(le_move_right_bt->get_state())
1558     {
1559     case BUTTON_PRESSED:
1560       pos_x += KEYBOARD_SPEED;
1561       show_minimap = true;
1562       break;
1563     case BUTTON_HOVER:
1564       pos_x += MOUSE_SPEED;
1565       show_minimap = true;
1566       break;
1567     case BUTTON_CLICKED:
1568       show_minimap = true;
1569       break;
1570     default:
1571       break;
1572     }
1573
1574     switch(le_move_up_bt->get_state())
1575     {
1576     case BUTTON_PRESSED:
1577       pos_y -= KEYBOARD_SPEED;
1578       show_minimap = true;
1579       break;
1580     case BUTTON_HOVER:
1581       pos_y -= MOUSE_SPEED;
1582       show_minimap = true;
1583       break;
1584     case BUTTON_CLICKED:
1585       show_minimap = true;
1586       break;
1587     default:
1588       break;
1589     }
1590
1591     switch(le_move_down_bt->get_state())
1592     {
1593     case BUTTON_PRESSED:
1594       pos_y += KEYBOARD_SPEED;
1595       show_minimap = true;
1596       break;
1597     case BUTTON_HOVER:
1598       pos_y += MOUSE_SPEED;
1599       show_minimap = true;
1600       break;
1601     case BUTTON_CLICKED:
1602       show_minimap = true;
1603       break;
1604     default:
1605       break;
1606     }
1607
1608       /* checking if pos_x and pos_y is within the limits... */
1609       if(pos_x > (le_world->get_level()->width * 32 + 32*2) - screen->w)
1610         pos_x = (le_world->get_level()->width * 32 + 32*2) - screen->w;
1611       if(pos_x < 0)
1612         pos_x = 0;
1613
1614       if(pos_y > (le_world->get_level()->height * 32) - screen->h)
1615         pos_y = (le_world->get_level()->height * 32) - screen->h;
1616       if(pos_y < 0)
1617         pos_y = 0;
1618
1619   }
1620
1621 }
1622
1623 void le_highlight_selection()
1624 {
1625   int x1, x2, y1, y2;
1626
1627   if(selection.x1 < selection.x2)
1628   {
1629     x1 = selection.x1;
1630     x2 = selection.x2;
1631   }
1632   else
1633   {
1634     x1 = selection.x2;
1635     x2 = selection.x1;
1636   }
1637   if(selection.y1 < selection.y2)
1638   {
1639     y1 = selection.y1;
1640     y2 = selection.y2;
1641   }
1642   else
1643   {
1644     y1 = selection.y2;
1645     y2 = selection.y1;
1646   }
1647
1648   x1 /= 32;
1649   x2 /= 32;
1650   y1 /= 32;
1651   y2 /= 32;
1652
1653   fillrect(x1*32-pos_x, y1*32-pos_y,32* (x2 - x1 + 1),32 * (y2 - y1 + 1),173,234,177,103);
1654 }
1655
1656 void le_change(float x, float y, int tm, unsigned int c)
1657 {
1658   if(le_world != NULL)
1659   {
1660     int xx,yy;
1661     int x1, x2, y1, y2;
1662
1663     le_level_changed = true;
1664
1665     switch(le_selection_mode)
1666     {
1667     case CURSOR:
1668       le_world->get_level()->change(x,y,tm,c);
1669
1670       base_type cursor_base;
1671       cursor_base.x = x;
1672       cursor_base.y = y;
1673       cursor_base.width = 32;
1674       cursor_base.height = 32;
1675
1676       /* if there is a bad guy over there, remove it */
1677       // XXX TODO
1678 #if 0
1679       for(std::list<BadGuy*>::iterator it = le_world->bad_guys.begin(); it != le_world->bad_guys.end(); ++it, ++i)
1680         if(rectcollision(cursor_base,(*it)->base))
1681         {
1682           delete (*it);
1683           le_world->bad_guys.erase(it);
1684           le_world->get_level()->badguy_data.erase(le_world->get_level()->badguy_data.begin() + i);
1685           break;
1686         }
1687 #endif
1688
1689       break;
1690     case SQUARE:
1691       if(selection.x1 < selection.x2)
1692       {
1693         x1 = selection.x1;
1694         x2 = selection.x2;
1695       }
1696       else
1697       {
1698         x1 = selection.x2;
1699         x2 = selection.x1;
1700       }
1701       if(selection.y1 < selection.y2)
1702       {
1703         y1 = selection.y1;
1704         y2 = selection.y2;
1705       }
1706       else
1707       {
1708         y1 = selection.y2;
1709         y2 = selection.y1;
1710       }
1711
1712       x1 /= 32;
1713       x2 /= 32;
1714       y1 /= 32;
1715       y2 /= 32;
1716
1717       /* if there is a bad guy over there, remove it */
1718       // TODO FIXME
1719 #if 0
1720       for(std::list<BadGuy*>::iterator it = le_world->bad_guys.begin();
1721           it != le_world->bad_guys.end(); /* will be at end of loop */)
1722       {
1723         if((*it)->base.x/32 >= x1 && (*it)->base.x/32 <= x2
1724             && (*it)->base.y/32 >= y1 && (*it)->base.y/32 <= y2)
1725         {
1726           delete (*it);
1727           it = le_world->bad_guys.erase(it);
1728           le_world->get_level()->badguy_data.erase(le_world->get_level()->badguy_data.begin() + i);
1729           continue;
1730         }
1731         else
1732         {
1733           ++i;
1734           ++it;
1735         }
1736       }
1737 #endif
1738
1739       for(xx = x1; xx <= x2; xx++)
1740         for(yy = y1; yy <= y2; yy++)
1741         {
1742           le_world->get_level()->change(xx*32, yy*32, tm, c);
1743
1744         }
1745       break;
1746     default:
1747       break;
1748     }
1749   }
1750 }
1751
1752 void le_testlevel()
1753 {
1754   //Make sure a time value is set when testing the level
1755   if(le_world->get_level()->time_left == 0)
1756     le_world->get_level()->time_left = 250;
1757
1758   le_world->get_level()->save("test", le_level, le_world);
1759
1760   GameSession session("test",le_level, ST_GL_TEST);
1761   session.run();
1762   player_status.reset();
1763
1764   music_manager->halt_music();
1765
1766   Menu::set_current(NULL);
1767   World::set_current(le_world);
1768 }
1769
1770 void le_showhelp()
1771 {
1772   bool tmp_show_grid = le_show_grid;
1773   SelectionMode temp_le_selection_mode = le_selection_mode;
1774   le_selection_mode = NONE;
1775   show_selections = true;
1776   le_show_grid = false;
1777   le_help_shown = true;
1778
1779   drawgradient(Color(0,0,0), Color(255,255,255));
1780   le_drawinterface();
1781
1782   SDL_Event event;
1783   unsigned int i, done_;
1784   char *text[] = {
1785
1786                    " - Supertux level editor tutorial - ",
1787                    "",
1788                    "To make your map, click the       ",
1789                    "tilegroup button and choose a     ",
1790                    "tilegroup.",
1791                    "Pick a tile and simply hold down  ",
1792                    "the left mouse button over the map",
1793                    "to \"paint\" your selection over",
1794                    "the screen.",
1795                    "",
1796                    "There are three layers for painting",
1797                    "tiles upon, Background layer,",
1798                    "the Interactive layer, and the",
1799                    "Foreground layer, which can be",
1800                    "toggled by the BkGrd, IntAct and",
1801                    "FrGrd buttons. The Foreground and",
1802                    "Background layers do not effect",
1803                    "Tux in the gameplay, but lie in",
1804                    "front of him or lie behind him in",
1805                    "his adventures.",
1806                  };
1807
1808   char *text2[] = {
1809
1810                     " - Supertux level editor tutorial - ",
1811                     "",
1812                     "The tiles placed on",
1813                     "the Interactive layer are those",
1814                     "which actually effect Tux in the",
1815                     "game.",
1816                     "",
1817                     "Click the objects menu to put ",
1818                     "bad guys and other objects in the",
1819                     "game. Unlike placing tiles, you",
1820                     "cannot \"paint\" enemies. Click",
1821                     "them onto the screen one at a time.",
1822                     "",
1823                     "To change the settings of your",
1824                     "level, click the button with the",
1825                     "screwdriver and wrench. From here",
1826                     "you can change the background,",
1827                     "music, length of the level,",
1828                     "and more."
1829                   };
1830
1831   char *text3[] = {
1832
1833                     " - Supertux level editor tutorial - ",
1834                     "",
1835                     "You may have more than one level.",
1836                     "Pressing the up and down buttons",
1837                     "above the button bar lets you",
1838                     "choose which one you are working on.",
1839                     "",
1840                     "If you would like to speed up your",
1841                     "level editing, a useful trick is",
1842                     "to learn the keyboard shortcuts.",
1843                     "They are easy to learn, just right-",
1844                     "click on the buttons.",
1845                     "",
1846                     "Have fun making levels! If you make",
1847                     "some good ones, send them to us on",
1848                     "the SuperTux mailing list!",
1849                     "- SuperTux team"
1850                   };
1851
1852
1853
1854   blue_text->drawf("- Help -", 0, 30, A_HMIDDLE, A_TOP, 2);
1855
1856   for(i = 0; i < sizeof(text)/sizeof(char *); i++)
1857     white_text->draw(text[i], 5, 80+(i*white_text->h), 1);
1858
1859   gold_text->drawf("Press Anything to Continue - Page 1/3", 0, 0, A_LEFT, A_BOTTOM, 1);
1860
1861   flipscreen();
1862
1863   done_ = 0;
1864
1865   while(done_ == 0)
1866   {
1867     done_ = wait_for_event(event);
1868     SDL_Delay(50);
1869   }
1870
1871   drawgradient(Color(0,0,0), Color(255,255,255));
1872   le_drawinterface();
1873
1874
1875   blue_text->drawf("- Help -", 0, 30, A_HMIDDLE, A_TOP, 2);
1876
1877   for(i = 0; i < sizeof(text2)/sizeof(char *); i++)
1878     white_text->draw(text2[i], 5, 80+(i*white_text->h), 1);
1879
1880   gold_text->drawf("Press Anything to Continue - Page 2/3", 0, 0, A_LEFT, A_BOTTOM, 1);
1881
1882   flipscreen();
1883
1884   done_ = 0;
1885
1886   while(done_ == 0)
1887   {
1888     done_ = wait_for_event(event);
1889     SDL_Delay(50);
1890   }
1891
1892   drawgradient(Color(0,0,0), Color(255,255,255));
1893   le_drawinterface();
1894
1895
1896   blue_text->drawf("- Help -", 0, 30, A_HMIDDLE, A_TOP, 2);
1897
1898   for(i = 0; i < sizeof(text3)/sizeof(char *); i++)
1899     white_text->draw(text3[i], 5, 80+(i*white_text->h), 1);
1900
1901   gold_text->drawf("Press Anything to Continue - Page 3/3", 0, 0, A_LEFT, A_BOTTOM, 1);
1902
1903   flipscreen();
1904
1905   done_ = 0;
1906
1907   while(done_ == 0)
1908   {
1909     done_ = wait_for_event(event);
1910     SDL_Delay(50);
1911   }
1912
1913   show_selections = true;
1914   le_show_grid = tmp_show_grid;
1915   le_selection_mode = temp_le_selection_mode;
1916   le_help_shown = false;
1917 }