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. */
92 int x, y, i; /* for cicles */
93 int pos_x, cursor_x, cursor_y, old_cursor_x, fire;
99 strcpy(levelfilename,"level1");
102 menumenu = MENU_LEVELEDITOR;
109 selection = load_image(DATA_PREFIX "/images/leveleditor/select.png", USE_ALPHA);
114 old_cursor_x = cursor_x;
118 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
122 SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, bkgd_red, bkgd_green, bkgd_blue));
124 keymod = SDL_GetModState();
126 while(SDL_PollEvent(&event))
128 // testing SDL_KEYDOWN, SDL_KEYUP and SDL_QUIT events
131 case SDL_KEYDOWN: // key pressed
132 key = event.key.keysym.sym;
142 cursor_x -= KEY_CURSOR_SPEED;
144 cursor_x -= KEY_CURSOR_FASTSPEED;
151 cursor_x += KEY_CURSOR_SPEED;
153 cursor_x += KEY_CURSOR_FASTSPEED;
155 if(cursor_x > (level_width*32) - 1)
156 cursor_x = (level_width*32) - 1;
160 cursor_y -= KEY_CURSOR_SPEED;
162 cursor_y -= KEY_CURSOR_FASTSPEED;
169 cursor_y += KEY_CURSOR_SPEED;
171 cursor_y += KEY_CURSOR_FASTSPEED;
173 if(cursor_y > 480-32)
186 cursor_x = (level_width * 32) - 32;
189 le_change(cursor_x, cursor_y, 0, '.');
192 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
193 le_change(cursor_x, cursor_y, 0, 'A');
195 le_change(cursor_x, cursor_y, 0, 'a');
198 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
199 le_change(cursor_x, cursor_y, 0, 'B');
202 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
203 le_change(cursor_x, cursor_y, 0, 'C');
205 le_change(cursor_x, cursor_y, 0, 'c');
208 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
209 le_change(cursor_x, cursor_y, 0, 'D');
211 le_change(cursor_x, cursor_y, 0, 'd');
214 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
215 le_change(cursor_x, cursor_y, 0, 'E');
217 le_change(cursor_x, cursor_y, 0, 'e');
220 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
221 le_change(cursor_x, cursor_y, 0, 'F');
223 le_change(cursor_x, cursor_y, 0, 'f');
226 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
227 le_change(cursor_x, cursor_y, 0, 'G');
229 le_change(cursor_x, cursor_y, 0, 'g');
232 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
233 le_change(cursor_x, cursor_y, 0, 'H');
235 le_change(cursor_x, cursor_y, 0, 'h');
238 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
239 le_change(cursor_x, cursor_y, 0, 'I');
241 le_change(cursor_x, cursor_y, 0, 'i');
244 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
245 le_change(cursor_x, cursor_y, 0, 'J');
247 le_change(cursor_x, cursor_y, 0, 'j');
250 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
251 le_change(cursor_x, cursor_y, 0, 'X');
253 le_change(cursor_x, cursor_y, 0, 'x');
256 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
257 le_change(cursor_x, cursor_y, 0, 'Y');
259 le_change(cursor_x, cursor_y, 0, 'y');
261 case SDLK_LEFTBRACKET:
262 le_change(cursor_x, cursor_y, 0, '[');
264 case SDLK_RIGHTBRACKET:
265 le_change(cursor_x, cursor_y, 0, ']');
269 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
270 le_change(cursor_x, cursor_y, 0, '#');
274 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
275 le_change(cursor_x, cursor_y, 0, '$');
278 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
279 le_change(cursor_x, cursor_y, 0, '|');
281 le_change(cursor_x, cursor_y, 0, '\\');
284 le_change(cursor_x, cursor_y, 0, '^');
288 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
289 le_change(cursor_x, cursor_y, 0, '&');
293 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
294 le_change(cursor_x, cursor_y, 0, '=');
295 else /* let's add a bad guy */
296 le_change(cursor_x, cursor_y, 0, '0');
298 for(i = 0; i < NUM_BAD_GUYS; ++i)
299 if (bad_guys[i].alive == NO)
301 bad_guys[i].alive = YES;
302 bad_guys[i].kind = BAD_BSOD;
303 bad_guys[i].x = (((int)cursor_x/32)*32);
304 bad_guys[i].y = (((int)cursor_y/32)*32);
309 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
310 le_change(cursor_x, cursor_y, 0, '!');
311 else /* let's add a bad guy */
312 le_change(cursor_x, cursor_y, 0, '1');
314 for(i = 0; i < NUM_BAD_GUYS; ++i)
315 if (bad_guys[i].alive == NO)
317 bad_guys[i].alive = YES;
318 bad_guys[i].kind = BAD_LAPTOP;
319 bad_guys[i].x = (((int)cursor_x/32)*32);
320 bad_guys[i].y = (((int)cursor_y/32)*32);
325 le_change(cursor_x, cursor_y, 0, '2');
327 for(i = 0; i < NUM_BAD_GUYS; ++i)
328 if (bad_guys[i].alive == NO)
330 bad_guys[i].alive = YES;
331 bad_guys[i].kind = BAD_MONEY;
332 bad_guys[i].x = (((int)cursor_x/32)*32);
333 bad_guys[i].y = (((int)cursor_y/32)*32);
338 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
339 le_change(cursor_x, cursor_y, 0, '*');
345 case SDL_KEYUP: // key released
346 switch(event.key.keysym.sym)
361 case SDL_MOUSEBUTTONDOWN:
362 if(event.button.button == SDL_BUTTON_LEFT)
367 cursor_x = ((int)(pos_x + x) / 32) * 32;
368 cursor_y = ((int) y / 32) * 32;
371 case SDL_MOUSEMOTION:
375 cursor_x = ((int)(pos_x + x) / 32) * 32;
376 cursor_y = ((int) y / 32) * 32;
378 case SDL_QUIT: // window closed
386 /* mouse movements */
388 if(x < MOUSE_LEFT_MARGIN)
389 pos_x -= MOUSE_POS_SPEED;
390 else if(x > MOUSE_RIGHT_MARGIN)
391 pos_x += MOUSE_POS_SPEED;
393 if(old_cursor_x != cursor_x)
395 if(cursor_x < pos_x + KEY_LEFT_MARGIN)
396 pos_x = cursor_x - KEY_LEFT_MARGIN;
398 if(cursor_x > pos_x + KEY_RIGHT_MARGIN)
399 pos_x = cursor_x - KEY_RIGHT_MARGIN;
404 if(pos_x > (level_width * 32) - 640)
405 pos_x = (level_width * 32) - 640;
407 old_cursor_x = cursor_x;
409 for (y = 0; y < 15; ++y)
410 for (x = 0; x < 21; ++x)
411 drawshape(x * 32, y * 32, tiles[y][x + (pos_x / 32)]);
413 /* Draw the Bad guys: */
414 for (i = 0; i < NUM_BAD_GUYS; ++i)
416 /* printf("\nbad_guys[%i].alive = %i", i, bad_guys[i].alive); */
417 if(bad_guys[i].alive == NO)
419 /* to support frames: img_bsod_left[(frame / 5) % 4] */
420 if(bad_guys[i].kind == BAD_BSOD)
421 drawimage(img_bsod_left[0], ((int)(bad_guys[i].x - pos_x)/32)*32, bad_guys[i].y, NO_UPDATE);
422 else if(bad_guys[i].kind == BAD_LAPTOP)
423 drawimage(img_laptop_left[0], ((int)(bad_guys[i].x - pos_x)/32)*32, bad_guys[i].y, NO_UPDATE);
424 else if (bad_guys[i].kind == BAD_MONEY)
425 drawimage(img_money_left[0], ((int)(bad_guys[i].x - pos_x)/32)*32, bad_guys[i].y, NO_UPDATE);
429 drawimage(selection, ((int)(cursor_x - pos_x)/32)*32, cursor_y, NO_UPDATE);
431 sprintf(str, "%d", time_left);
432 drawtext("TIME", 324, 0, letters_blue, NO_UPDATE, 1);
433 drawtext(str, 404, 0, letters_gold, NO_UPDATE, 1);
435 sprintf(str, "%s", levelname);
436 drawtext("NAME", 0, 0, letters_blue, NO_UPDATE, 1);
437 drawtext(str, 80, 0, letters_gold, NO_UPDATE, 1);
439 drawtext("F1 for Help", 10, 430, letters_blue, NO_UPDATE, 1);
447 if(done == DONE_QUIT)
457 SDL_FreeSurface(selection);
459 /*if(done == DONE_SAVE)*/ /* let's save the changes */
462 if(done == DONE_CHANGELEVEL) change level
463 return leveleditor(level);
468 void le_change(int x, int y, int sx, unsigned char c)
474 xx = ((x + sx) / 32);
476 /* if there is a bad guy over there, remove it */
477 for(i = 0; i < NUM_BAD_GUYS; ++i)
478 if (bad_guys[i].alive)
479 if(xx == bad_guys[i].x/32 && yy == bad_guys[i].y/32)
480 bad_guys[i].alive = NO;
483 if (yy >= 0 && yy <= 15 && xx >= 0 && xx <= level_width)
487 /* Save data for this level: */
495 /* Save data file: */
497 filename = (char *) malloc(sizeof(char) * (strlen(DATA_PREFIX) + 20));
498 sprintf(filename, "%s/levels/%s.dat", DATA_PREFIX, levelfilename);
499 fi = fopen(filename, "w");
510 /* sptrinf("# Level created by SuperTux built-in editor", fi); */
512 fputs(levelname, fi);
514 fputs(leveltheme, fi);
516 sprintf(str, "%d\n", time_left); /* time */
518 fputs(song_title, fi); /* song filename */
519 sprintf(str, "%d\n", bkgd_red); /* red background color */
521 sprintf(str, "%d\n", bkgd_green); /* green background color */
523 sprintf(str, "%d\n", bkgd_blue); /* blue background color */
525 sprintf(str, "%d\n", level_width); /* level width */
528 for(y = 0; y < 15; ++y)
536 drawcenteredtext("SAVED!", 240, letters_gold, NO_UPDATE, 1);
542 /* I had to copy loadlevel, because loadlevel changes the tiles.
543 For intance, badguys are removed from the tiles and we have to
546 /* Load data for this level: */
548 void le_loadlevel(void)
554 char* line = malloc(sizeof(char)*10);/*[LEVEL_WIDTH + 5];*/
556 /* Load data file: */
558 filename = (char *) malloc(sizeof(char) * (strlen(DATA_PREFIX) + 20));
559 sprintf(filename, "%s/levels/%s.dat", DATA_PREFIX, levelfilename);
560 fi = fopen(filename, "r");
572 strcpy(levelname, str);
573 levelname[strlen(levelname)-1] = '\0';
577 strcpy(leveltheme, str);
578 leveltheme[strlen(leveltheme)-1] = '\0';
581 time_left = atoi(line);
584 strcpy(song_title,str);
586 bkgd_red = atoi(line);
588 bkgd_green= atoi(line);
590 bkgd_blue = atoi(line);
592 level_width = atoi(line);
595 line = malloc(level_width*sizeof(char)+5);
597 for (y = 0; y < 15; ++y)
599 fgets(line, level_width + 5, fi);
600 line[strlen(line) - 1] = '\0';
602 tiles[y] = malloc((strlen(line)+5)*sizeof(char));
603 strcpy(tiles[y], line);
608 /* Activate bad guys: */
610 /* as oposed to the gameloop.c func, this one doesn't remove
611 the badguys from tiles */
613 for (y = 0; y < 15; ++y)
614 for (x = 0; x < level_width; ++x)
615 if (tiles[y][x] >= '0' && tiles[y][x] <= '9')
616 add_bad_guy(x * 32, y * 32, tiles[y][x] - '0');
627 clearscreen(0, 0, 0);
629 sprintf(str, "Editing Level %s", levelfilename);
630 drawcenteredtext(str, 200, letters_red, NO_UPDATE, 1);
632 sprintf(str, "%s", levelname);
633 drawcenteredtext(str, 224, letters_gold, NO_UPDATE, 1);
664 "./Del - Remove tile",
669 drawcenteredtext("- Help -", 30, letters_red, NO_UPDATE, 1);
670 drawtext("Keys:", 80, 60, letters_gold, NO_UPDATE, 1);
672 for(i = 0; i < sizeof(text)/sizeof(char *); i++)
673 drawtext(text[i], 40, 90+(i*16), letters_blue, NO_UPDATE, 1);
680 while(SDL_PollEvent(&event))
683 case SDL_KEYDOWN: // key pressed