many code-cleanups. merged leveleditor patch from Ricardo Cruz. Fixed bugs. many...
[supertux.git] / src / leveleditor.c
1 /***************************************************************************
2  *                                                                         *
3  *   This program is free software; you can redistribute it and/or modify  *
4  *   it under the terms of the GNU General Public License as published by  *
5  *   the Free Software Foundation; either version 2 of the License, or     *
6  *   (at your option) any later version.                                   *
7  *                                                                         *
8  ***************************************************************************/
9
10 /*  December 28, 2003 - February 1st, 2004 */
11
12 /* leveleditor.c - A built-in level editor for SuperTux
13  by Ricardo Cruz <rick2@aeiou.pt>                      */
14
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <errno.h>
19 #include <unistd.h>
20 #include <SDL.h>
21 #include <SDL_image.h>
22 #include "leveleditor.h"
23
24 #include "screen.h"
25 #include "defines.h"
26 #include "globals.h"
27 #include "setup.h"
28 #include "menu.h"
29 #include "level.h"
30 #include "badguy.h"
31 #include "scene.h"
32 #include "button.h"
33
34 /* definitions to aid development */
35 #define DONE_LEVELEDITOR 1
36 #define DONE_QUIT        2
37 #define DONE_CHANGELEVEL 3
38
39 /* definitions that affect gameplay */
40 #define KEY_CURSOR_SPEED 32
41 #define KEY_CURSOR_FASTSPEED 64
42
43 /* when pagedown/up pressed speed:*/
44 #define PAGE_CURSOR_SPEED 13*32
45
46 #define MOUSE_LEFT_MARGIN 80
47 #define MOUSE_RIGHT_MARGIN (560-32)
48 /* right_margin should noticed that the cursor is 32 pixels,
49    so it should subtract that value */
50 #define MOUSE_POS_SPEED 20
51
52 /* look */
53 #define SELECT_W 2 // size of the selections lines
54 #define SELECT_CLR 0, 255, 0, 255  // lines color (R, G, B, A)
55
56 /* gameloop funcs declerations */
57
58 void loadshared(void);
59 void unloadshared(void);
60
61 /* own declerations */
62 /* crutial ones (main loop) */
63 int le_init();
64 void le_quit();
65 void le_drawlevel();
66 void le_checkevents();
67 void le_change(float x, float y, unsigned char c);
68 void le_testlevel();
69 void le_showhelp();
70 void le_set_defaults(void);
71 void le_activate_bad_guys(void);
72
73 void le_highlight_selection();
74
75 /* leveleditor internals */
76 static int le_level_changed;  /* if changes, ask for saving, when quiting*/
77 static int pos_x, cursor_x, cursor_y, cursor_tile, fire;
78 static int le_level;
79 static st_level le_current_level;
80 static st_subset le_level_subset;
81 static int le_show_grid;
82 static int le_frame;
83 static texture_type le_selection;
84 static int done;
85 static char le_current_tile;
86 static int le_mouse_pressed;
87 static button_type le_test_level_bt;
88 static button_type le_next_level_bt;
89 static button_type le_previous_level_bt;
90 static button_type le_rubber_bt;
91 static button_type le_select_mode_one_bt;
92 static button_type le_select_mode_two_bt;
93 static button_type le_bad_bsod_bt;
94 static button_panel_type le_bt_panel;
95
96 static square selection;
97 static int le_selection_mode;
98 static SDL_Event event;
99
100 void le_activate_bad_guys(void)
101 {
102   int x,y;
103
104   /* Activate bad guys: */
105
106   /* as oposed to the gameloop.c func, this one doesn't remove
107   the badguys from tiles                                    */
108
109   for (y = 0; y < 15; ++y)
110     for (x = 0; x < le_current_level.width; ++x)
111       if (le_current_level.tiles[y][x] >= '0' && le_current_level.tiles[y][x] <= '9')
112         add_bad_guy(x * 32, y * 32, le_current_level.tiles[y][x] - '0');
113 }
114
115 void le_set_defaults()
116 {
117   /* Set defaults: */
118
119   if(le_current_level.time_left == 0)
120     le_current_level.time_left = 255;
121 }
122
123 /* FIXME: Needs to be implemented. It should ask the user for the level(file)name and then let him create a new level based on this. */
124 void newlevel()
125 {}
126
127 /* FIXME: It should let select the user a level, which is in the leveldirectory and then load it. */
128 void selectlevel()
129 {}
130
131 int leveleditor(int levelnb)
132 {
133   int last_time, now_time;
134
135   le_level = levelnb;
136   if(le_init() != 0)
137     return 1;
138
139   while(YES)
140     {
141       last_time = SDL_GetTicks();
142       le_frame++;
143
144       le_checkevents();
145
146       /* making events results to be in order */
147       if(pos_x < 0)
148         pos_x = 0;
149       if(pos_x > (le_current_level.width * 32) - screen->w)
150         pos_x = (le_current_level.width * 32) - screen->w;
151
152       /* draw the level */
153       le_drawlevel();
154
155       if(show_menu)
156         {
157           menu_process_current();
158           if(current_menu == &leveleditor_menu)
159             {
160               switch (menu_check(&leveleditor_menu))
161                 {
162                 case 0:
163                   show_menu = NO;
164                   break;
165                 case 4:
166                   done = DONE_LEVELEDITOR;
167                   break;
168                 }
169             }
170         }
171
172       if(done)
173         {
174           le_quit();
175           return 0;
176         }
177
178       if(done == DONE_QUIT)
179         {
180           le_quit();
181           return 1;
182         }
183
184       SDL_Delay(25);
185       now_time = SDL_GetTicks();
186       if (now_time < last_time + FPS)
187         SDL_Delay(last_time + FPS - now_time);  /* delay some time */
188
189       flipscreen();
190     }
191
192   return done;
193 }
194
195 int le_init()
196 {
197   subset_load(&le_level_subset,"default");
198   le_show_grid = YES;
199
200   /*  level_changed = NO;*/
201   fire = DOWN;
202   done = 0;
203   menu_reset();
204   menu_set_current(&leveleditor_menu);
205   le_frame = 0; /* support for frames in some tiles, like waves and bad guys */
206
207   arrays_init();
208   loadshared();
209   le_set_defaults();
210
211   le_level_changed = NO;
212   if(level_load(&le_current_level, le_level_subset.name, le_level) != 0)
213     {
214       le_quit();
215       return 1;
216     }
217   if(le_current_level.time_left == 0)
218     le_current_level.time_left = 255;
219
220   level_load_gfx(&le_current_level);
221
222   le_current_tile = '.';
223   le_mouse_pressed = NO;
224   le_activate_bad_guys();
225
226   texture_load(&le_selection,DATA_PREFIX "/images/leveleditor/select.png", USE_ALPHA);
227
228   /* Load buttons */
229   button_load(&le_test_level_bt,"/images/icons/test-level.png","Test Level",SDLK_F4,150,screen->h - 64);
230   button_load(&le_next_level_bt,"/images/icons/up.png","Test Level", SDLK_PAGEUP,screen->w-64,0);
231   button_load(&le_previous_level_bt,"/images/icons/down.png","Test Level",SDLK_PAGEDOWN,screen->w-32,0);
232   button_load(&le_rubber_bt,"/images/icons/rubber.png","Rubber",SDLK_DELETE,screen->w-64,32);
233   button_load(&le_select_mode_one_bt,"/images/icons/select-mode1.png","Select Tile",SDLK_F3,screen->w-64,16);
234   button_load(&le_select_mode_two_bt,"/images/icons/select-mode2.png","Select Tiles",SDLK_F3,screen->w-32,16);
235   button_load(&le_bad_bsod_bt,"/images/shared/bsod-left-1.png","Select Tiles",'0',screen->w-32,32);
236   button_panel_init(&le_bt_panel, 500,100, 64, 400);
237   button_panel_additem(&le_bt_panel, button_create("/images/shared/bsod-left-1.png","Select Tiles",'0',screen->w-32,32));
238
239   SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
240
241   return 0;
242 }
243
244 void le_goto_level(int levelnb)
245 {
246
247   level_free(&le_current_level);
248   if(level_load(&le_current_level, le_level_subset.name, levelnb) != 0)
249     {
250       level_load(&le_current_level, le_level_subset.name, le_level);
251     }
252   else
253     {
254       le_level = levelnb;
255     }
256   if(le_current_level.time_left == 0)
257     le_current_level.time_left = 255;
258
259   level_free_gfx();
260   level_load_gfx(&le_current_level);
261
262   le_activate_bad_guys();
263 }
264
265 void le_quit(void)
266 {
267   /*if(level_changed == YES)
268     if(askforsaving() == CANCEL)
269       return;*/ //FIXME
270
271   SDL_EnableKeyRepeat(0, 0);    // disables key repeating
272
273   button_free(&le_test_level_bt);
274   level_free_gfx();
275   level_free(&le_current_level);
276   unloadshared();
277   arrays_free();
278   texture_free(&le_selection);
279 }
280
281 void le_drawlevel()
282 {
283   int y,x,i,s;
284   static char str[LEVEL_NAME_MAX];
285
286   /* Draw the real background */
287   if(le_current_level.bkgd_image[0] != '\0')
288     {
289       s = pos_x / 30;
290       texture_draw_part(&img_bkgd,s,0,0,0,img_bkgd.w - s - 32, img_bkgd.h, NO_UPDATE);
291       texture_draw_part(&img_bkgd,0,0,screen->w - s - 32 ,0,s,img_bkgd.h, NO_UPDATE);
292     }
293   else
294     {
295       clearscreen(le_current_level.bkgd_red, le_current_level.bkgd_green, le_current_level.bkgd_blue);
296     }
297
298   /*       clearscreen(current_level.bkgd_red, current_level.bkgd_green, current_level.bkgd_blue); */
299
300   for (y = 0; y < 15; ++y)
301     for (x = 0; x < 20; ++x)
302       {
303         drawshape(x * 32 - ((int)pos_x % 32), y * 32, le_current_level.tiles[y][x + (int)(pos_x / 32)]);
304
305         /* draw whats inside stuff when cursor is selecting those */
306         /* (draw them all the time - is this the right behaviour?) */
307         switch(le_current_level.tiles[y][x + (int)(pos_x/32)])
308           {
309           case 'B':
310             texture_draw(&img_mints, x * 32 - ((int)pos_x % 32), y*32, NO_UPDATE);
311             break;
312           case '!':
313             texture_draw(&img_golden_herring, x * 32 - ((int)pos_x % 32), y*32, NO_UPDATE);
314             break;
315           case 'x':
316           case 'y':
317           case 'A':
318             texture_draw(&img_distro[(frame / 5) % 4], x * 32 - ((int)pos_x % 32), y*32, NO_UPDATE);
319             break;
320           default:
321             break;
322           }
323       }
324
325   /* Draw the Bad guys: */
326   for (i = 0; i < num_bad_guys; ++i)
327     {
328       if(bad_guys[i].base.alive == NO)
329         continue;
330       /* to support frames: img_bsod_left[(frame / 5) % 4] */
331       if(bad_guys[i].kind == BAD_BSOD)
332         texture_draw(&img_bsod_left[(le_frame / 5) % 4], bad_guys[i].base.x - pos_x, bad_guys[i].base.y, NO_UPDATE);
333       else if(bad_guys[i].kind == BAD_LAPTOP)
334         texture_draw(&img_laptop_left[(le_frame / 5) % 3], bad_guys[i].base.x - pos_x, bad_guys[i].base.y, NO_UPDATE);
335       else if (bad_guys[i].kind == BAD_MONEY)
336         texture_draw(&img_money_left[(le_frame / 5) % 2], bad_guys[i].base.x - pos_x, bad_guys[i].base.y, NO_UPDATE);
337     }
338
339   /* Draw the player: */
340   /* for now, the position is fixed at (0, 240) */
341   texture_draw(&tux_right[(frame / 5) % 3], 0 - pos_x, 240, NO_UPDATE);
342
343   /* draw a grid (if selected) */
344   if(le_show_grid)
345     {
346       for(x = 0; x < 19; x++)
347         fillrect(x*32 - ((int)pos_x % 32), 0, 1, screen->h, 225, 225, 225,255);
348       for(y = 0; y < 15; y++)
349         fillrect(0, y*32, screen->w - 32, 1, 225, 225, 225,255);
350     }
351
352   if(le_selection_mode == CURSOR)
353     texture_draw(&le_selection, cursor_x - pos_x, cursor_y, NO_UPDATE);
354   else if(le_selection_mode == SQUARE)
355     {
356       int w, h;
357       le_highlight_selection();
358       /* draw current selection */
359       w = selection.x2 - selection.x1;
360       h = selection.y2 - selection.y1;
361       fillrect(selection.x1 - pos_x, selection.y1, w, SELECT_W, SELECT_CLR);
362       fillrect(selection.x1 - pos_x + w, selection.y1, SELECT_W, h, SELECT_CLR);
363       fillrect(selection.x1 - pos_x, selection.y1 + h, w, SELECT_W, SELECT_CLR);
364       fillrect(selection.x1 - pos_x, selection.y1, SELECT_W, h, SELECT_CLR);
365     }
366
367
368   /* draw button bar */
369   fillrect(screen->w - 64, 0, 64, screen->h, 50, 50, 50,255);
370   drawshape(19 * 32, 14 * 32, le_current_tile);
371
372   button_draw(&le_test_level_bt);
373   button_draw(&le_next_level_bt);
374   button_draw(&le_previous_level_bt);
375   button_draw(&le_rubber_bt);
376   button_draw(&le_select_mode_one_bt);
377   button_draw(&le_select_mode_two_bt);
378   button_draw(&le_bad_bsod_bt);
379   button_panel_draw(&le_bt_panel);
380   
381   sprintf(str, "%d", le_current_level.time_left);
382   text_draw(&white_text, "TIME", 324, 0, 1, NO_UPDATE);
383   text_draw(&gold_text, str, 404, 0, 1, NO_UPDATE);
384
385   text_draw(&white_text, "NAME", 0, 0, 1, NO_UPDATE);
386   text_draw(&gold_text, le_current_level.name, 80, 0, 1, NO_UPDATE);
387
388   sprintf(str, "%d/%d", le_level,le_level_subset.levels);
389   text_draw(&white_text, "NUMB", 0, 20, 1, NO_UPDATE);
390   text_draw(&gold_text, str, 80, 20, 1, NO_UPDATE);
391
392   text_draw(&white_small_text, "F1 for Help", 10, 430, 1, NO_UPDATE);
393   text_draw(&white_small_text, "F2 for Testing", 150, 430, 1, NO_UPDATE);
394 }
395
396 void le_checkevents()
397 {
398   SDLKey key;
399   SDLMod keymod;
400   int x,y;
401
402   keymod = SDL_GetModState();
403
404   while(SDL_PollEvent(&event))
405     {
406       /* testing SDL_KEYDOWN, SDL_KEYUP and SDL_QUIT events*/
407       if(event.type == SDL_KEYDOWN || ((event.type == SDL_MOUSEBUTTONDOWN || SDL_MOUSEMOTION) && (event.motion.x > 0 && event.motion.x < screen->w - 64 &&
408                                        event.motion.y > 0 && event.motion.y < screen->h)))
409         {
410
411           switch(event.type)
412             {
413             case SDL_KEYDOWN:   // key pressed
414               key = event.key.keysym.sym;
415               if(show_menu)
416                 {
417                   menu_event(&event.key.keysym);
418                   break;
419                 }
420               switch(key)
421                 {
422                 case SDLK_ESCAPE:
423                   if(!show_menu)
424                     show_menu = YES;
425                   else
426                     show_menu = NO;
427                   break;
428                 case SDLK_LEFT:
429                   if(fire == DOWN)
430                     cursor_x -= KEY_CURSOR_SPEED;
431                   else
432                     cursor_x -= KEY_CURSOR_FASTSPEED;
433
434                   if(cursor_x < pos_x + MOUSE_LEFT_MARGIN)
435                     pos_x = cursor_x - MOUSE_LEFT_MARGIN;
436
437                   break;
438                 case SDLK_RIGHT:
439                   if(fire == DOWN)
440                     cursor_x += KEY_CURSOR_SPEED;
441                   else
442                     cursor_x += KEY_CURSOR_FASTSPEED;
443
444                   if(cursor_x > pos_x + MOUSE_RIGHT_MARGIN-32)
445                     pos_x = cursor_x - MOUSE_RIGHT_MARGIN+32;
446
447                   break;
448                 case SDLK_UP:
449                   if(fire == DOWN)
450                     cursor_y -= KEY_CURSOR_SPEED;
451                   else
452                     cursor_y -= KEY_CURSOR_FASTSPEED;
453
454                   if(cursor_y < 0)
455                     cursor_y = 0;
456                   break;
457                 case SDLK_DOWN:
458                   if(fire == DOWN)
459                     cursor_y += KEY_CURSOR_SPEED;
460                   else
461                     cursor_y += KEY_CURSOR_FASTSPEED;
462
463                   if(cursor_y > screen->h-32)
464                     cursor_y = screen->h-32;
465                   break;
466                 case SDLK_LCTRL:
467                   fire =UP;
468                   break;
469                 case SDLK_F1:
470                   le_showhelp();
471                   break;
472                 case SDLK_HOME:
473                   cursor_x = 0;
474                   pos_x = cursor_x;
475                   break;
476                 case SDLK_END:
477                   cursor_x = (le_current_level.width * 32) - 32;
478                   pos_x = cursor_x;
479                   break;
480                 case SDLK_PAGEUP:
481                   cursor_x -= PAGE_CURSOR_SPEED;
482
483                   if(cursor_x < pos_x + MOUSE_LEFT_MARGIN)
484                     pos_x = cursor_x - MOUSE_LEFT_MARGIN;
485
486                   break;
487                 case SDLK_PAGEDOWN:
488                   cursor_x += PAGE_CURSOR_SPEED;
489
490                   if(cursor_x > pos_x + MOUSE_RIGHT_MARGIN-32)
491                     pos_x = cursor_x - MOUSE_RIGHT_MARGIN+32;
492
493                   break;
494                 case SDLK_F9:
495                   le_show_grid = !le_show_grid;
496                   break;
497                 case SDLK_PERIOD:
498                   le_change(cursor_x, cursor_y, '.');
499                   break;
500                 case SDLK_a:
501                   if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
502                     le_current_tile = 'A';
503                   else
504                     le_current_tile = 'a';
505                   break;
506                 case SDLK_b:
507                   if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
508                     le_change(cursor_x, cursor_y, 'B');
509                   break;
510                 case SDLK_c:
511                   if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
512                     le_change(cursor_x, cursor_y, 'C');
513                   else
514                     le_change(cursor_x, cursor_y, 'c');
515                   break;
516                 case SDLK_d:
517                   if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
518                     le_change(cursor_x, cursor_y, 'D');
519                   else
520                     le_change(cursor_x, cursor_y, 'd');
521                   break;
522                 case SDLK_e:
523                   if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
524                     le_change(cursor_x, cursor_y, 'E');
525                   else
526                     le_change(cursor_x, cursor_y, 'e');
527                   break;
528                 case SDLK_f:
529                   if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
530                     le_change(cursor_x, cursor_y, 'F');
531                   else
532                     le_change(cursor_x, cursor_y, 'f');
533                   break;
534                 case SDLK_g:
535                   if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
536                     le_change(cursor_x, cursor_y, 'G');
537                   else
538                     le_change(cursor_x, cursor_y, 'g');
539                   break;
540                 case SDLK_h:
541                   if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
542                     le_change(cursor_x, cursor_y, 'H');
543                   else
544                     le_change(cursor_x, cursor_y, 'h');
545                   break;
546                 case SDLK_i:
547                   if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
548                     le_change(cursor_x, cursor_y, 'I');
549                   else
550                     le_change(cursor_x, cursor_y, 'i');
551                   break;
552                 case SDLK_j:
553                   if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
554                     le_change(cursor_x, cursor_y, 'J');
555                   else
556                     le_change(cursor_x, cursor_y, 'j');
557                   break;
558                 case SDLK_x:
559                   if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
560                     le_change(cursor_x, cursor_y, 'X');
561                   else
562                     le_change(cursor_x, cursor_y, 'x');
563                   break;
564                 case SDLK_y:
565                   if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
566                     le_change(cursor_x, cursor_y, 'Y');
567                   else
568                     le_change(cursor_x, cursor_y, 'y');
569                   break;
570                 case SDLK_LEFTBRACKET:
571                   le_change(cursor_x, cursor_y, '[');
572                   break;
573                 case SDLK_RIGHTBRACKET:
574                   le_change(cursor_x, cursor_y, ']');
575                   break;
576                 case SDLK_HASH:
577                 case SDLK_3:
578                   if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
579                     le_change(cursor_x, cursor_y, '#');
580                   break;
581                 case SDLK_DOLLAR:
582                 case SDLK_4:
583                   if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
584                     le_change(cursor_x, cursor_y, '$');
585                   break;
586                 case SDLK_BACKSLASH:
587                   if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
588                     le_change(cursor_x, cursor_y, '|');
589                   else
590                     le_change(cursor_x, cursor_y, '\\');
591                   break;
592                 case SDLK_CARET:
593                   le_change(cursor_x, cursor_y, '^');
594                   break;
595                 case SDLK_AMPERSAND:
596                 case SDLK_6:
597                   if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
598                     le_change(cursor_x, cursor_y, '&');
599                   break;
600                 case SDLK_EQUALS:
601                 case SDLK_0:
602                   if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
603                     le_change(cursor_x, cursor_y, '=');
604                   else          /* let's add a bad guy */
605                     le_change(cursor_x, cursor_y, '0');
606                   break;
607                 case SDLK_1:
608                   if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
609                     le_change(cursor_x, cursor_y, '!');
610                   else          /* let's add a bad guy */
611                     le_change(cursor_x, cursor_y, '1');
612                   break;
613                 case SDLK_2:
614                   le_change(cursor_x, cursor_y, '2');
615                   break;
616                 case SDLK_PLUS:
617                   if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
618                     le_change(cursor_x, cursor_y, '*');
619                   break;
620                 default:
621                   break;
622                 }
623               break;
624             case SDL_KEYUP:     // key released
625               switch(event.key.keysym.sym)
626                 {
627                 case SDLK_LCTRL:
628                   fire = DOWN;
629                   break;
630                 default:
631                   break;
632                 }
633               break;
634             case SDL_MOUSEBUTTONDOWN:
635               if(event.button.button == SDL_BUTTON_LEFT)
636                 {
637                   le_mouse_pressed = YES;
638
639                   selection.x1 = event.motion.x + pos_x;
640                   selection.y1 = event.motion.y;
641                   selection.x2 = event.motion.x + pos_x;
642                   selection.y2 = event.motion.y;
643                 }
644               break;
645             case SDL_MOUSEBUTTONUP:
646               if(event.button.button == SDL_BUTTON_LEFT)
647                 {
648                   le_mouse_pressed = NO;
649                 }
650               break;
651             case SDL_MOUSEMOTION:
652               if(!show_menu)
653                 {
654                   x = event.motion.x;
655                   y = event.motion.y;
656
657                   cursor_x = ((int)(pos_x + x) / 32) * 32;
658                   cursor_y = ((int) y / 32) * 32;
659
660                   if(le_mouse_pressed == YES)
661                     {
662                       selection.x2 = x + pos_x;
663                       selection.y2 = y;
664                     }
665                 }
666               break;
667             case SDL_QUIT:      // window closed
668               done = DONE_QUIT;
669               break;
670             default:
671               break;
672             }
673         }
674
675
676     }
677
678
679   if(event.type == SDL_KEYDOWN || ((event.type == SDL_MOUSEBUTTONDOWN || SDL_MOUSEMOTION) && (event.motion.x > screen->w-64 && event.motion.x < screen->w &&
680                                    event.motion.y > 0 && event.motion.y < screen->h)))
681     {
682       /* Check for button events */
683       button_event(&le_test_level_bt,&event);
684       if(button_get_state(&le_test_level_bt) == BN_CLICKED)
685         le_testlevel();
686       button_event(&le_next_level_bt,&event);
687       if(button_get_state(&le_next_level_bt) == BN_CLICKED)
688         {
689           if(le_level < le_level_subset.levels)
690             le_goto_level(++le_level);
691         }
692       button_event(&le_previous_level_bt,&event);
693       if(button_get_state(&le_previous_level_bt) == BN_CLICKED)
694         {
695           if(le_level > 1)
696             le_goto_level(--le_level);
697         }
698       button_event(&le_rubber_bt,&event);
699       if(button_get_state(&le_rubber_bt) == BN_CLICKED)
700         le_current_tile = '.';
701       button_event(&le_select_mode_one_bt,&event);
702       if(button_get_state(&le_select_mode_one_bt) == BN_CLICKED)
703         le_selection_mode = CURSOR;
704       button_event(&le_select_mode_two_bt,&event);
705       if(button_get_state(&le_select_mode_two_bt) == BN_CLICKED)
706         le_selection_mode = SQUARE;
707       button_event(&le_bad_bsod_bt,&event);
708       if(button_get_state(&le_bad_bsod_bt) == BN_CLICKED)
709         le_current_tile = '0';
710     }
711
712   if(le_mouse_pressed)
713     {
714       le_change(cursor_x, cursor_y, le_current_tile);
715     }
716
717 }
718
719 void le_highlight_selection()
720 {
721   int x,y,i;
722   int x1, x2, y1, y2;
723
724   if(selection.x1 < selection.x2)
725     {
726       x1 = selection.x1;
727       x2 = selection.x2;
728     }
729   else
730     {
731       x1 = selection.x2;
732       x2 = selection.x1;
733     }
734   if(selection.y1 < selection.y2)
735     {
736       y1 = selection.y1;
737       y2 = selection.y2;
738     }
739   else
740     {
741       y1 = selection.y2;
742       y2 = selection.y1;
743     }
744
745   x1 /= 32;
746   x2 /= 32;
747   y1 /= 32;
748   y2 /= 32;
749
750   fillrect(x1*32-pos_x, y1*32,32* (x2 - x1 + 1),32 * (y2 - y1 + 1),173,234,177,103);
751 }
752
753 void le_change(float x, float y, unsigned char c)
754 {
755   int xx,yy,i;
756   int x1, x2, y1, y2;
757
758   /*  level_changed = YES; */
759
760   switch(le_selection_mode)
761     {
762     case CURSOR:
763       level_change(&le_current_level,x,y,c);
764
765       yy = ((int)y / 32);
766       xx = ((int)x / 32);
767
768       /* if there is a bad guy over there, remove it */
769       for(i = 0; i < num_bad_guys; ++i)
770         if (bad_guys[i].base.alive)
771           if(xx == bad_guys[i].base.x/32 && yy == bad_guys[i].base.y/32)
772             bad_guys[i].base.alive = NO;
773
774       if(c == '0')  /* if it's a bad guy */
775         add_bad_guy(xx*32, yy*32, BAD_BSOD);
776       else if(c == '1')
777         add_bad_guy(xx*32, yy*32, BAD_LAPTOP);
778       else if(c == '2')
779         add_bad_guy(xx*32, yy*32, BAD_MONEY);
780
781       break;
782     case SQUARE:
783       if(selection.x1 < selection.x2)
784         {
785           x1 = selection.x1;
786           x2 = selection.x2;
787         }
788       else
789         {
790           x1 = selection.x2;
791           x2 = selection.x1;
792         }
793       if(selection.y1 < selection.y2)
794         {
795           y1 = selection.y1;
796           y2 = selection.y2;
797         }
798       else
799         {
800           y1 = selection.y2;
801           y2 = selection.y1;
802         }
803
804       x1 /= 32;
805       x2 /= 32;
806       y1 /= 32;
807       y2 /= 32;
808
809       /* if there is a bad guy over there, remove it */
810       for(i = 0; i < num_bad_guys; ++i)
811         if(bad_guys[i].base.alive)
812           if(bad_guys[i].base.x/32 >= x1 && bad_guys[i].base.x/32 <= x2
813               && bad_guys[i].base.y/32 >= y1 && bad_guys[i].base.y/32 <= y2)
814             bad_guys[i].base.alive = NO;
815
816       for(xx = x1; xx <= x2; xx++)
817         for(yy = y1; yy <= y2; yy++)
818           {
819             level_change(&le_current_level, xx*32, yy*32, c);
820
821             if(c == '0')  // if it's a bad guy
822               add_bad_guy(xx*32, yy*32, BAD_BSOD);
823             else if(c == '1')
824               add_bad_guy(xx*32, yy*32, BAD_LAPTOP);
825             else if(c == '2')
826               add_bad_guy(xx*32, yy*32, BAD_MONEY);
827           }
828       break;
829     default:
830       break;
831     }
832 }
833
834 void le_testlevel()
835 {
836   level_save(&le_current_level,"test",le_level);
837   gameloop("test",le_level, ST_GL_TEST);
838   menu_set_current(&leveleditor_menu);
839   arrays_init();
840   level_load_gfx(&le_current_level);
841   loadshared();
842   le_activate_bad_guys();
843 }
844
845 void le_showhelp()
846 {
847   SDL_Event event;
848   int i, done;
849   char *text[] = {
850                    "X/x - Brick0",
851                    "Y/y - Brick1",
852                    "A/B/! - Box full",
853                    "a - Box empty",
854                    "C-F - Cloud0",
855                    "c-f - Cloud1",
856                    "G-J - Bkgd0",
857                    "g-j - Bkgd1",
858                    "# - Solid0",
859                    "[ - Solid1",
860                    "= - Solid2",
861                    "] - Solid3",
862                    "$ - Distro",
863                    "^ - Waves",
864                    "* - Poletop",
865                    "| - Pole",
866                    "\\ - Flag",
867                    "& - Water",
868                    "0-2 - BadGuys",
869                    "./Del - Remove tile",
870                    "F9 - Show/Hide Grid",
871                    "F3 - Change Selection Mode",
872                    "Esc - Menu"};
873
874
875   text_drawf(&blue_text, "- Help -", 0, 30, A_HMIDDLE, A_TOP, 2, NO_UPDATE);
876   text_draw(&gold_text, "Keys:", 80, 60, 1, NO_UPDATE);
877
878   for(i = 0; i < sizeof(text)/sizeof(char *); i++)
879     text_draw(&white_text, text[i], 40, 90+(i*16), 1, NO_UPDATE);
880
881   text_drawf(&gold_text, "Press Any Key to Continue", 0, 460, A_HMIDDLE, A_TOP, 1, NO_UPDATE);
882
883   flipscreen();
884
885   done = 0;
886
887   while(done == 0)
888     while(SDL_PollEvent(&event))
889       switch(event.type)
890         {
891         case SDL_MOUSEBUTTONDOWN:               // mouse pressed
892         case SDL_KEYDOWN:               // key pressed
893           done = 1;
894           break;
895         default:
896           break;
897         }
898 }