arrays are dynamic now, fixed bugs, more code cleanups
[supertux.git] / src / leveleditor.c
1
2 /***************************************************************************
3  *                                                                         *
4  *   This program is free software; you can redistribute it and/or modify  *
5  *   it under the terms of the GNU General Public License as published by  *
6  *   the Free Software Foundation; either version 2 of the License, or     *
7  *   (at your option) any later version.                                   *
8  *                                                                         *
9  ***************************************************************************/
10
11 /*  December 28, 2003 - January 1st, 2004 */
12
13 /* leveleditor.c - A built-in level editor for SuperTux
14  by Ricardo Cruz <rick2@aeiou.pt>                      */
15
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <errno.h>
20 #include <unistd.h>
21 #include <SDL.h>
22 #include <SDL_image.h>
23 #include "leveleditor.h"
24
25 #include "screen.h"
26 #include "defines.h"
27 #include "globals.h"
28 #include "setup.h"
29 #include "menu.h"
30 #include "level.h"
31 #include "badguy.h"
32 #include "gameloop.h"
33 #include "scene.h"
34
35 /* definitions to aid development */
36 #define DONE_LEVELEDITOR    1
37 #define DONE_QUIT        2
38 #define DONE_CHANGELEVEL  3
39
40 /* definitions that affect gameplay */
41 #define KEY_CURSOR_SPEED 32
42 #define KEY_CURSOR_FASTSPEED 64
43
44 #define CURSOR_LEFT_MARGIN 96
45 #define CURSOR_RIGHT_MARGIN 512
46 /* right_margin should noticed that the cursor is 32 pixels,
47    so it should subtract that value */
48
49 #define MOUSE_LEFT_MARGIN 32
50 #define MOUSE_RIGHT_MARGIN 608
51 #define MOUSE_POS_SPEED 32
52
53 /* Level Intro: */
54 /*
55   clearscreen(0, 0, 0);
56  
57   sprintf(str, "Editing Level %s", levelfilename);
58   drawcenteredtext(str, 200, letters_red, NO_UPDATE, 1);
59  
60   sprintf(str, "%s", levelname);
61   drawcenteredtext(str, 224, letters_gold, NO_UPDATE, 1);
62  
63   flipscreen();
64  
65   SDL_Delay(1000);
66 */
67
68 /* gameloop funcs declerations */
69
70 void loadshared(void);
71 void unloadshared(void);
72
73 /* own declerations */
74
75 void savelevel();
76 void le_change(float x, float y, unsigned char c);
77 void showhelp();
78 void le_set_defaults(void);
79 void le_activate_bad_guys(void);
80
81 /* global variables (based on the gameloop ones) */
82
83 int level;
84 st_level current_level;
85 char level_subset[100];
86
87 int frame;
88 texture_type selection;
89 int last_time, now_time;
90
91 void le_quit(void)
92 {
93   unloadlevelgfx();
94   unloadshared();
95   arrays_free();
96   texture_free(&selection);
97 }
98
99 void le_activate_bad_guys(void)
100 {
101   int x,y;
102
103   /* Activate bad guys: */
104
105   /* as oposed to the gameloop.c func, this one doesn't remove
106   the badguys from tiles                                    */
107
108   for (y = 0; y < 15; ++y)
109     for (x = 0; x < current_level.width; ++x)
110       if (current_level.tiles[y][x] >= '0' && current_level.tiles[y][x] <= '9')
111         add_bad_guy(x * 32, y * 32, current_level.tiles[y][x] - '0');
112 }
113
114 void le_set_defaults()
115 {
116   /* Set defaults: */
117
118   if(current_level.time_left == 0)
119     current_level.time_left = 255;
120 }
121
122 /* 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. */
123 void newlevel()
124 {}
125
126 /* FIXME: It should let select the user a level, which is in the leveldirectory and then load it. */
127 void selectlevel()
128 {}
129
130 int leveleditor()
131 {
132   char str[LEVEL_NAME_MAX];
133   int done;
134   int x, y, i;  /* for cicles */
135   int pos_x, cursor_x, cursor_y, fire;
136   SDL_Event event;
137   SDLKey key;
138   SDLMod keymod;
139
140   strcpy(level_subset,"default");
141
142   level = 1;
143
144   initmenu();
145   menumenu = MENU_LEVELEDITOR;
146   show_menu = YES;
147
148   frame = 0;    /* support for frames in some tiles, like waves and bad guys */
149
150   arrays_init();
151   
152   loadshared();
153   set_defaults();
154
155   loadlevel(&current_level,"default",level);
156   loadlevelgfx(&current_level);
157
158   le_activate_bad_guys();
159   le_set_defaults();
160
161   texture_load(&selection,DATA_PREFIX "/images/leveleditor/select.png", USE_ALPHA);
162
163   done = 0;
164   pos_x = 0;
165   cursor_x = 3*32;
166   cursor_y = 2*32;
167   fire = DOWN;
168
169   SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
170
171   while(1)
172     {
173       clearscreen(current_level.bkgd_red, current_level.bkgd_green, current_level.bkgd_blue);
174
175       last_time = SDL_GetTicks();
176       frame++;
177
178       keymod = SDL_GetModState();
179
180       while(SDL_PollEvent(&event))
181         {
182           // testing SDL_KEYDOWN, SDL_KEYUP and SDL_QUIT events
183           switch(event.type)
184             {
185             case SDL_KEYDOWN:   // key pressed
186               key = event.key.keysym.sym;
187               if(show_menu)
188                 {
189                   menu_event(key);
190                   break;
191                 }
192               switch(key)
193                 {
194                 case SDLK_LEFT:
195                   if(fire == DOWN)
196                     cursor_x -= KEY_CURSOR_SPEED;
197                   else
198                     cursor_x -= KEY_CURSOR_FASTSPEED;
199
200                   if(cursor_x < 0)
201                     cursor_x = 0;
202                   break;
203                 case SDLK_RIGHT:
204                   if(fire == DOWN)
205                     cursor_x += KEY_CURSOR_SPEED;
206                   else
207                     cursor_x += KEY_CURSOR_FASTSPEED;
208
209                   if(cursor_x > (current_level.width*32) - 1)
210                     cursor_x = (current_level.width*32) - 1;
211                   break;
212                 case SDLK_UP:
213                   if(fire == DOWN)
214                     cursor_y -= KEY_CURSOR_SPEED;
215                   else
216                     cursor_y -= KEY_CURSOR_FASTSPEED;
217
218                   if(cursor_y < 0)
219                     cursor_y = 0;
220                   break;
221                 case SDLK_DOWN:
222                   if(fire == DOWN)
223                     cursor_y += KEY_CURSOR_SPEED;
224                   else
225                     cursor_y += KEY_CURSOR_FASTSPEED;
226
227                   if(cursor_y > screen->h-32)
228                     cursor_y = screen->h-32;
229                   break;
230                 case SDLK_LCTRL:
231                   fire =UP;
232                   break;
233                 case SDLK_F1:
234                   showhelp();
235                   break;
236                 case SDLK_HOME:
237                   cursor_x = 0;
238                   break;
239                 case SDLK_END:
240                   cursor_x = (current_level.width * 32) - 32;
241                   break;
242                 case SDLK_PERIOD:
243                   le_change(cursor_x, cursor_y, '.');
244                   break;
245                 case SDLK_a:
246                   if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
247                     le_change(cursor_x, cursor_y, 'A');
248                   else
249                     le_change(cursor_x, cursor_y, 'a');
250                   break;
251                 case SDLK_b:
252                   if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
253                     le_change(cursor_x, cursor_y, 'B');
254                   break;
255                 case SDLK_c:
256                   if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
257                     le_change(cursor_x, cursor_y, 'C');
258                   else
259                     le_change(cursor_x, cursor_y, 'c');
260                   break;
261                 case SDLK_d:
262                   if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
263                     le_change(cursor_x, cursor_y, 'D');
264                   else
265                     le_change(cursor_x, cursor_y, 'd');
266                   break;
267                 case SDLK_e:
268                   if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
269                     le_change(cursor_x, cursor_y, 'E');
270                   else
271                     le_change(cursor_x, cursor_y, 'e');
272                   break;
273                 case SDLK_f:
274                   if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
275                     le_change(cursor_x, cursor_y, 'F');
276                   else
277                     le_change(cursor_x, cursor_y, 'f');
278                   break;
279                 case SDLK_g:
280                   if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
281                     le_change(cursor_x, cursor_y, 'G');
282                   else
283                     le_change(cursor_x, cursor_y, 'g');
284                   break;
285                 case SDLK_h:
286                   if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
287                     le_change(cursor_x, cursor_y, 'H');
288                   else
289                     le_change(cursor_x, cursor_y, 'h');
290                   break;
291                 case SDLK_i:
292                   if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
293                     le_change(cursor_x, cursor_y, 'I');
294                   else
295                     le_change(cursor_x, cursor_y, 'i');
296                   break;
297                 case SDLK_j:
298                   if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
299                     le_change(cursor_x, cursor_y, 'J');
300                   else
301                     le_change(cursor_x, cursor_y, 'j');
302                   break;
303                 case SDLK_x:
304                   if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
305                     le_change(cursor_x, cursor_y, 'X');
306                   else
307                     le_change(cursor_x, cursor_y, 'x');
308                   break;
309                 case SDLK_y:
310                   if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
311                     le_change(cursor_x, cursor_y, 'Y');
312                   else
313                     le_change(cursor_x, cursor_y, 'y');
314                   break;
315                 case SDLK_LEFTBRACKET:
316                   le_change(cursor_x, cursor_y, '[');
317                   break;
318                 case SDLK_RIGHTBRACKET:
319                   le_change(cursor_x, cursor_y, ']');
320                   break;
321                 case SDLK_HASH:
322                 case SDLK_3:
323                   if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
324                     le_change(cursor_x, cursor_y, '#');
325                   break;
326                 case SDLK_DOLLAR:
327                 case SDLK_4:
328                   if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
329                     le_change(cursor_x, cursor_y, '$');
330                   break;
331                 case SDLK_BACKSLASH:
332                   if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
333                     le_change(cursor_x, cursor_y, '|');
334                   else
335                     le_change(cursor_x, cursor_y, '\\');
336                   break;
337                 case SDLK_CARET:
338                   le_change(cursor_x, cursor_y, '^');
339                   break;
340                 case SDLK_AMPERSAND:
341                 case SDLK_6:
342                   if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
343                     le_change(cursor_x, cursor_y, '&');
344                   break;
345                 case SDLK_EQUALS:
346                 case SDLK_0:
347                   if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
348                     le_change(cursor_x, cursor_y, '=');
349                   else          /* let's add a bad guy */
350                     le_change(cursor_x, cursor_y, '0');
351                  
352                   add_bad_guy((((int)cursor_x/32)*32), (((int)cursor_y/32)*32), BAD_BSOD);
353                   break;
354                 case SDLK_1:
355                   if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
356                     le_change(cursor_x, cursor_y, '!');
357                   else          /* let's add a bad guy */
358                     le_change(cursor_x, cursor_y, '1');
359
360                   add_bad_guy((((int)cursor_x/32)*32), (((int)cursor_y/32)*32), BAD_LAPTOP);
361                   break;
362                 case SDLK_2:
363                   le_change(cursor_x, cursor_y, '2');
364
365                   add_bad_guy((((int)cursor_x/32)*32), (((int)cursor_y/32)*32), BAD_MONEY);
366                   break;
367                 case SDLK_PLUS:
368                   if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
369                     le_change(cursor_x, cursor_y, '*');
370                   break;
371                 default:
372                   break;
373                 }
374               break;
375             case SDL_KEYUP:     // key released
376               switch(event.key.keysym.sym)
377                 {
378                 case SDLK_LCTRL:
379                   fire = DOWN;
380                   break;
381                 case SDLK_ESCAPE:
382                   if(!show_menu)
383                     show_menu = YES;
384                   else
385                     show_menu = NO;
386                   break;
387                 default:
388                   break;
389                 }
390               break;
391               /*            case SDL_MOUSEBUTTONDOWN:
392                             if(event.button.button == SDL_BUTTON_LEFT)
393                               {
394               This will draw current tile in the cursor position, when the interface is done.
395                               }
396                             break;*/
397             case SDL_MOUSEMOTION:
398               if(!show_menu)
399                 {
400                   x = event.motion.x;
401                   y = event.motion.y;
402
403                   cursor_x = ((int)(pos_x + x) / 32) * 32;
404                   cursor_y = ((int) y / 32) * 32;
405                 }
406               break;
407             case SDL_QUIT:      // window closed
408               done = DONE_QUIT;
409               break;
410             default:
411               break;
412             }
413         }
414
415       /* mouse movements */
416       /*      x = event.motion.x;
417             if(x < MOUSE_LEFT_MARGIN)
418               pos_x -= MOUSE_POS_SPEED;
419             else if(x > MOUSE_RIGHT_MARGIN)
420               pos_x += MOUSE_POS_SPEED;*/
421
422
423       if(cursor_x < pos_x + CURSOR_LEFT_MARGIN)
424         pos_x = cursor_x - CURSOR_LEFT_MARGIN;
425
426       if(cursor_x > pos_x + CURSOR_RIGHT_MARGIN)
427         pos_x = cursor_x - CURSOR_RIGHT_MARGIN;
428
429       if(pos_x < 0)
430         pos_x = 0;
431       if(pos_x > (current_level.width * 32) - screen->w)
432         pos_x = (current_level.width * 32) - screen->w;
433
434       for (y = 0; y < 15; ++y)
435         for (x = 0; x < 21; ++x)
436           drawshape(x * 32, y * 32, current_level.tiles[y][x + (pos_x / 32)]);
437
438       /* Draw the Bad guys: */
439       for (i = 0; i < num_bad_guys; ++i)
440         {
441           /* printf("\nbad_guys[%i].alive = %i", i, bad_guys[i].alive); */
442           if(bad_guys[i].base.alive == NO)
443             continue;
444           /* to support frames: img_bsod_left[(frame / 5) % 4] */
445           if(bad_guys[i].kind == BAD_BSOD)
446             texture_draw(&img_bsod_left[(frame / 5) % 4], ((int)(bad_guys[i].base.x - pos_x)/32)*32, bad_guys[i].base.y, NO_UPDATE);
447           else if(bad_guys[i].kind == BAD_LAPTOP)
448             texture_draw(&img_laptop_left[(frame / 5) % 3], ((int)(bad_guys[i].base.x - pos_x)/32)*32, bad_guys[i].base.y, NO_UPDATE);
449           else if (bad_guys[i].kind == BAD_MONEY)
450             texture_draw(&img_money_left[(frame / 5) % 2], ((int)(bad_guys[i].base.x - pos_x)/32)*32, bad_guys[i].base.y, NO_UPDATE);
451         }
452
453
454       texture_draw(&selection, ((int)(cursor_x - pos_x)/32)*32, cursor_y, NO_UPDATE);
455
456       sprintf(str, "%d", current_level.time_left);
457       drawtext("TIME", 324, 0, letters_blue, NO_UPDATE, 1);
458       drawtext(str, 404, 0, letters_gold, NO_UPDATE, 1);
459
460       sprintf(str, "%s", current_level.name);
461       drawtext("NAME", 0, 0, letters_blue, NO_UPDATE, 1);
462       drawtext(str, 80, 0, letters_gold, NO_UPDATE, 1);
463
464       drawtext("F1 for Help", 10, 430, letters_blue, NO_UPDATE, 1);
465
466       if(show_menu)
467         {
468           done = drawmenu();
469           if(done)
470           {
471             le_quit();
472             return 0;
473           }
474         }
475       if(done == DONE_QUIT)
476         {
477         le_quit();
478         return 1;
479         }
480
481       SDL_Delay(50);
482       now_time = SDL_GetTicks();
483       if (now_time < last_time + FPS)
484         SDL_Delay(last_time + FPS - now_time);  /* delay some time */
485
486       flipscreen();
487     }
488
489   return done;
490 }
491
492 void le_change(float x, float y, unsigned char c)
493 {
494 int i;
495 int xx, yy;
496
497   level_change(&current_level,x,y,c);
498
499   yy = (y / 32);
500   xx = (x / 32);
501
502   /* if there is a bad guy over there, remove it */
503   for(i = 0; i < num_bad_guys; ++i)
504     if (bad_guys[i].base.alive)
505       if(xx == bad_guys[i].base.x/32 && yy == bad_guys[i].base.y/32)
506         bad_guys[i].base.alive = NO;
507 }
508
509 /* Save data for this level: */
510 void savelevel(void)
511 {
512   FILE * fi;
513   char * filename;
514   int y;
515   char str[80];
516
517   /* Save data file: */
518
519   filename = (char *) malloc(sizeof(char) * (strlen(DATA_PREFIX) + 20) + strlen(level_subset));
520   sprintf(filename, "%s/levels/%s/level%d.dat", DATA_PREFIX, level_subset, level);
521   fi = fopen(filename, "w");
522   if (fi == NULL)
523     {
524       perror(filename);
525       st_shutdown();
526       free(filename);
527       exit(-1);
528     }
529   free(filename);
530
531
532   /* sptrinf("# Level created by SuperTux built-in editor", fi); */
533
534   fputs(current_level.name, fi);
535   fputs("\n", fi);
536   fputs(current_level.theme, fi);
537   fputs("\n", fi);
538   sprintf(str, "%d\n", current_level.time_left);        /* time */
539   fputs(str, fi);
540   fputs(current_level.song_title, fi);  /* song filename */
541   sprintf(str, "\n%d\n", current_level.bkgd_red);       /* red background color */
542   fputs(str, fi);
543   sprintf(str, "%d\n", current_level.bkgd_green);       /* green background color */
544   fputs(str, fi);
545   sprintf(str, "%d\n", current_level.bkgd_blue);        /* blue background color */
546   fputs(str, fi);
547   sprintf(str, "%d\n", current_level.width);    /* level width */
548   fputs(str, fi);
549
550   for(y = 0; y < 15; ++y)
551     {
552       fputs(current_level.tiles[y], fi);
553       fputs("\n", fi);
554     }
555
556   fclose(fi);
557
558   drawcenteredtext("SAVED!", 240, letters_gold, NO_UPDATE, 1);
559   flipscreen();
560   SDL_Delay(1000);
561 }
562
563 void showhelp()
564 {
565   SDL_Event event;
566   int done;
567   char *text[] = {
568                    "X/x - Brick0",
569                    "Y/y - Brick1",
570                    "A/B/! - Box full",
571                    "a - Box empty",
572                    "C-F - Cloud0",
573                    "c-f - Cloud1",
574                    "G-J - Bkgd0",
575                    "g-j - Bkgd1",
576                    "# - Solid0",
577                    "[ - Solid1",
578                    "= - Solid2",
579                    "] - Solid3",
580                    "$ - Distro",
581                    "^ - Waves",
582                    "* - Poletop",
583                    "| - Pole",
584                    "\\ - Flag",
585                    "& - Water",
586                    "0-2 - BadGuys",
587                    "./Del - Remove tile",
588                    "Esc - Menu"};
589
590
591   drawcenteredtext("- Help -", 30, letters_red, NO_UPDATE, 2);
592   drawtext("Keys:", 80, 60, letters_gold, NO_UPDATE, 1);
593
594   int i;
595   for(i = 0; i < sizeof(text)/sizeof(char *); i++)
596     drawtext(text[i], 40, 90+(i*16), letters_blue, NO_UPDATE, 1);
597
598   drawcenteredtext("Press Any Key to Continue", 460, letters_gold, NO_UPDATE, 1);
599
600   flipscreen();
601
602   done = 0;
603
604   while(done == 0)
605     while(SDL_PollEvent(&event))
606       switch(event.type)
607         {
608         case SDL_MOUSEBUTTONDOWN:               // mouse pressed
609         case SDL_KEYDOWN:               // key pressed
610           done = 1;
611           break;
612         default:
613           break;
614         }
615 }