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