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