2 /***************************************************************************
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. *
9 ***************************************************************************/
11 /* December 28, 2003 - December 29, 2003 */
13 /* leveleditor.c - A built-in level editor for SuperTux
14 by Ricardo Cruz <rick2@aeiou.pt> */
22 #include <SDL_image.h>
24 #include "leveleditor.h"
36 bad_guy_type bad_guys[NUM_BAD_GUYS];
37 SDL_Surface *img_bsod_left[4], *img_laptop_left[3], *img_money_left[2];
39 char levelfilename[100];
42 unsigned char* tiles[15];
43 int bkgd_red, bkgd_green, bkgd_blue, level_width;
45 SDL_Surface *selection;
47 /* definitions to aid development */
48 #define DONE_LEVELEDITOR 1
50 #define DONE_CHANGELEVEL 3
52 /* definitions that affect gameplay */
53 #define KEY_CURSOR_SPEED 32
54 #define KEY_CURSOR_FASTSPEED 64
55 #define KEY_LEFT_MARGIN 160
56 #define KEY_RIGHT_MARGIN 480
58 #define MOUSE_LEFT_MARGIN 32
59 #define MOUSE_RIGHT_MARGIN 608
60 #define MOUSE_POS_SPEED 32
62 /* gameloop funcs declerations */
64 void loadlevelgfx(void);
65 void unloadlevelgfx(void);
66 void add_bad_guy(int x, int y, int kind);
67 void loadshared(void);
68 void unloadshared(void);
69 void drawshape(int x, int y, unsigned char c);
71 /* own declerations */
74 void le_loadlevel(void);
75 void le_change(int x, int y, int sx, unsigned char c);
78 /* 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. */
83 /* FIXME: It should let select the user a level, which is in the leveldirectory and then load it. */
93 strcpy(levelfilename,"level1");
96 menumenu = MENU_LEVELEDITOR;
103 selection = load_image(DATA_PREFIX "/images/leveleditor/select.png", USE_ALPHA);
108 int x, y, i; /* for cicles */
109 int pos_x, cursor_x, cursor_y, old_cursor_x, fire;
113 old_cursor_x = cursor_x;
117 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
125 SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, bkgd_red, bkgd_green, bkgd_blue));
127 keymod = SDL_GetModState();
129 while(SDL_PollEvent(&event))
131 // testing SDL_KEYDOWN, SDL_KEYUP and SDL_QUIT events
134 case SDL_KEYDOWN: // key pressed
135 key = event.key.keysym.sym;
145 cursor_x -= KEY_CURSOR_SPEED;
147 cursor_x -= KEY_CURSOR_FASTSPEED;
154 cursor_x += KEY_CURSOR_SPEED;
156 cursor_x += KEY_CURSOR_FASTSPEED;
158 if(cursor_x > (level_width*32) - 1)
159 cursor_x = (level_width*32) - 1;
163 cursor_y -= KEY_CURSOR_SPEED;
165 cursor_y -= KEY_CURSOR_FASTSPEED;
172 cursor_y += KEY_CURSOR_SPEED;
174 cursor_y += KEY_CURSOR_FASTSPEED;
176 if(cursor_y > 480-32)
189 cursor_x = (level_width * 32) - 32;
192 le_change(cursor_x, cursor_y, 0, '.');
195 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
196 le_change(cursor_x, cursor_y, 0, 'A');
198 le_change(cursor_x, cursor_y, 0, 'a');
201 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
202 le_change(cursor_x, cursor_y, 0, 'B');
205 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
206 le_change(cursor_x, cursor_y, 0, 'C');
208 le_change(cursor_x, cursor_y, 0, 'c');
211 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
212 le_change(cursor_x, cursor_y, 0, 'D');
214 le_change(cursor_x, cursor_y, 0, 'd');
217 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
218 le_change(cursor_x, cursor_y, 0, 'E');
220 le_change(cursor_x, cursor_y, 0, 'e');
223 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
224 le_change(cursor_x, cursor_y, 0, 'F');
226 le_change(cursor_x, cursor_y, 0, 'f');
229 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
230 le_change(cursor_x, cursor_y, 0, 'G');
232 le_change(cursor_x, cursor_y, 0, 'g');
235 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
236 le_change(cursor_x, cursor_y, 0, 'H');
238 le_change(cursor_x, cursor_y, 0, 'h');
241 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
242 le_change(cursor_x, cursor_y, 0, 'I');
244 le_change(cursor_x, cursor_y, 0, 'i');
247 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
248 le_change(cursor_x, cursor_y, 0, 'J');
250 le_change(cursor_x, cursor_y, 0, 'j');
253 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
254 le_change(cursor_x, cursor_y, 0, 'X');
256 le_change(cursor_x, cursor_y, 0, 'x');
259 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
260 le_change(cursor_x, cursor_y, 0, 'Y');
262 le_change(cursor_x, cursor_y, 0, 'y');
264 case SDLK_LEFTBRACKET:
265 le_change(cursor_x, cursor_y, 0, '[');
267 case SDLK_RIGHTBRACKET:
268 le_change(cursor_x, cursor_y, 0, ']');
272 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
273 le_change(cursor_x, cursor_y, 0, '#');
277 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
278 le_change(cursor_x, cursor_y, 0, '$');
281 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
282 le_change(cursor_x, cursor_y, 0, '|');
284 le_change(cursor_x, cursor_y, 0, '\\');
287 le_change(cursor_x, cursor_y, 0, '^');
291 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
292 le_change(cursor_x, cursor_y, 0, '&');
296 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
297 le_change(cursor_x, cursor_y, 0, '=');
298 else /* let's add a bad guy */
299 le_change(cursor_x, cursor_y, 0, '0');
301 for(i = 0; i < NUM_BAD_GUYS; ++i)
302 if (bad_guys[i].alive == NO)
304 bad_guys[i].alive = YES;
305 bad_guys[i].kind = BAD_BSOD;
306 bad_guys[i].x = (((int)cursor_x/32)*32);
307 bad_guys[i].y = (((int)cursor_y/32)*32);
312 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
313 le_change(cursor_x, cursor_y, 0, '!');
314 else /* let's add a bad guy */
315 le_change(cursor_x, cursor_y, 0, '1');
317 for(i = 0; i < NUM_BAD_GUYS; ++i)
318 if (bad_guys[i].alive == NO)
320 bad_guys[i].alive = YES;
321 bad_guys[i].kind = BAD_LAPTOP;
322 bad_guys[i].x = (((int)cursor_x/32)*32);
323 bad_guys[i].y = (((int)cursor_y/32)*32);
328 le_change(cursor_x, cursor_y, 0, '2');
330 for(i = 0; i < NUM_BAD_GUYS; ++i)
331 if (bad_guys[i].alive == NO)
333 bad_guys[i].alive = YES;
334 bad_guys[i].kind = BAD_MONEY;
335 bad_guys[i].x = (((int)cursor_x/32)*32);
336 bad_guys[i].y = (((int)cursor_y/32)*32);
341 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
342 le_change(cursor_x, cursor_y, 0, '*');
348 case SDL_KEYUP: // key released
349 switch(event.key.keysym.sym)
364 case SDL_MOUSEBUTTONDOWN:
365 if(event.button.button == SDL_BUTTON_LEFT)
370 cursor_x = ((int)(pos_x + x) / 32) * 32;
371 cursor_y = ((int) y / 32) * 32;
374 case SDL_MOUSEMOTION:
378 cursor_x = ((int)(pos_x + x) / 32) * 32;
379 cursor_y = ((int) y / 32) * 32;
381 case SDL_QUIT: // window closed
389 /* mouse movements */
391 if(x < MOUSE_LEFT_MARGIN)
392 pos_x -= MOUSE_POS_SPEED;
393 else if(x > MOUSE_RIGHT_MARGIN)
394 pos_x += MOUSE_POS_SPEED;
396 if(old_cursor_x != cursor_x)
398 if(cursor_x < pos_x + KEY_LEFT_MARGIN)
399 pos_x = cursor_x - KEY_LEFT_MARGIN;
401 if(cursor_x > pos_x + KEY_RIGHT_MARGIN)
402 pos_x = cursor_x - KEY_RIGHT_MARGIN;
407 if(pos_x > (level_width * 32) - 640)
408 pos_x = (level_width * 32) - 640;
410 old_cursor_x = cursor_x;
412 for (y = 0; y < 15; ++y)
413 for (x = 0; x < 21; ++x)
414 drawshape(x * 32, y * 32, tiles[y][x + (pos_x / 32)]);
416 /* Draw the Bad guys: */
417 for (i = 0; i < NUM_BAD_GUYS; ++i)
419 /* printf("\nbad_guys[%i].alive = %i", i, bad_guys[i].alive); */
420 if(bad_guys[i].alive == NO)
422 /* to support frames: img_bsod_left[(frame / 5) % 4] */
423 if(bad_guys[i].kind == BAD_BSOD)
424 drawimage(img_bsod_left[0], ((int)(bad_guys[i].x - pos_x)/32)*32, bad_guys[i].y, NO_UPDATE);
425 else if(bad_guys[i].kind == BAD_LAPTOP)
426 drawimage(img_laptop_left[0], ((int)(bad_guys[i].x - pos_x)/32)*32, bad_guys[i].y, NO_UPDATE);
427 else if (bad_guys[i].kind == BAD_MONEY)
428 drawimage(img_money_left[0], ((int)(bad_guys[i].x - pos_x)/32)*32, bad_guys[i].y, NO_UPDATE);
432 drawimage(selection, ((int)(cursor_x - pos_x)/32)*32, cursor_y, NO_UPDATE);
434 sprintf(str, "%d", time_left);
435 drawtext("TIME", 324, 0, letters_blue, NO_UPDATE, 1);
436 drawtext(str, 404, 0, letters_gold, NO_UPDATE, 1);
438 sprintf(str, "%s", levelname);
439 drawtext("NAME", 0, 0, letters_blue, NO_UPDATE, 1);
440 drawtext(str, 80, 0, letters_gold, NO_UPDATE, 1);
442 drawtext("F1 for Help", 10, 430, letters_blue, NO_UPDATE, 1);
450 if(done == DONE_QUIT)
460 SDL_FreeSurface(selection);
462 /*if(done == DONE_SAVE)*/ /* let's save the changes */
465 if(done == DONE_CHANGELEVEL) change level
466 return leveleditor(level);
471 void le_change(int x, int y, int sx, unsigned char c)
476 xx = ((x + sx) / 32);
478 /* if there is a bad guy over there, remove it */
480 for(i = 0; i < NUM_BAD_GUYS; ++i)
481 if (bad_guys[i].alive)
482 if(xx == bad_guys[i].x/32 && yy == bad_guys[i].y/32)
483 bad_guys[i].alive = NO;
486 if (yy >= 0 && yy <= 15 && xx >= 0 && xx <= level_width)
490 /* Save data for this level: */
498 /* Save data file: */
500 filename = (char *) malloc(sizeof(char) * (strlen(DATA_PREFIX) + 20));
501 sprintf(filename, "%s/levels/%s.dat", DATA_PREFIX, levelfilename);
502 fi = fopen(filename, "w");
513 /* sptrinf("# Level created by SuperTux built-in editor", fi); */
515 fputs(levelname, fi);
517 fputs(leveltheme, fi);
519 sprintf(str, "%d\n", time_left); /* time */
521 fputs(song_title, fi); /* song filename */
522 sprintf(str, "%d\n", bkgd_red); /* red background color */
524 sprintf(str, "%d\n", bkgd_green); /* green background color */
526 sprintf(str, "%d\n", bkgd_blue); /* blue background color */
528 sprintf(str, "%d\n", level_width); /* level width */
532 for(y = 0; y < 15; ++y)
540 drawcenteredtext("SAVED!", 240, letters_gold, NO_UPDATE, 1);
546 /* I had to copy loadlevel, because loadlevel changes the tiles.
547 For intance, badguys are removed from the tiles and we have to
550 /* Load data for this level: */
552 void le_loadlevel(void)
558 char* line = malloc(sizeof(char)*10);/*[LEVEL_WIDTH + 5];*/
560 /* Load data file: */
562 filename = (char *) malloc(sizeof(char) * (strlen(DATA_PREFIX) + 20));
563 sprintf(filename, "%s/levels/%s.dat", DATA_PREFIX, levelfilename);
564 fi = fopen(filename, "r");
576 strcpy(levelname, str);
577 levelname[strlen(levelname)-1] = '\0';
581 strcpy(leveltheme, str);
582 leveltheme[strlen(leveltheme)-1] = '\0';
585 time_left = atoi(line);
588 strcpy(song_title,str);
590 bkgd_red = atoi(line);
592 bkgd_green= atoi(line);
594 bkgd_blue = atoi(line);
596 level_width = atoi(line);
599 line = malloc(level_width*sizeof(char)+5);
602 for (y = 0; y < 15; ++y)
604 fgets(line, level_width + 5, fi);
605 line[strlen(line) - 1] = '\0';
607 tiles[y] = malloc((strlen(line)+5)*sizeof(char));
608 strcpy(tiles[y], line);
613 /* Activate bad guys: */
615 /* as oposed to the gameloop.c func, this one doesn't remove
616 the badguys from tiles */
618 for (y = 0; y < 15; ++y)
619 for (x = 0; x < level_width; ++x)
620 if (tiles[y][x] >= '0' && tiles[y][x] <= '9')
621 add_bad_guy(x * 32, y * 32, tiles[y][x] - '0');
632 clearscreen(0, 0, 0);
634 sprintf(str, "Editing Level %s", levelfilename);
635 drawcenteredtext(str, 200, letters_red, NO_UPDATE, 1);
637 sprintf(str, "%s", levelname);
638 drawcenteredtext(str, 224, letters_gold, NO_UPDATE, 1);
647 drawcenteredtext("- Help -", 30, letters_red, NO_UPDATE, 1);
648 drawtext("Keys:", 80, 60, letters_gold, NO_UPDATE, 1);
670 "./Del - Remove tile",
674 for(i = 0; i < sizeof(text)/sizeof(char *); i++)
675 drawtext(text[i], 40, 90+(i*16), letters_blue, NO_UPDATE, 1);
684 while(SDL_PollEvent(&event))
687 case SDL_KEYDOWN: // key pressed