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 30, 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"
34 /* definitions to aid development */
35 #define DONE_LEVELEDITOR 1
37 #define DONE_CHANGELEVEL 3
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
45 #define MOUSE_LEFT_MARGIN 32
46 #define MOUSE_RIGHT_MARGIN 608
47 #define MOUSE_POS_SPEED 32
53 sprintf(str, "Editing Level %s", levelfilename);
54 drawcenteredtext(str, 200, letters_red, NO_UPDATE, 1);
56 sprintf(str, "%s", levelname);
57 drawcenteredtext(str, 224, letters_gold, NO_UPDATE, 1);
64 /* global variables (based on the gameloop ones) */
66 bad_guy_type bad_guys[NUM_BAD_GUYS];
67 SDL_Surface *img_bsod_left[4], *img_laptop_left[3], *img_money_left[2];
69 st_level current_level;
70 char level_subset[100];
73 SDL_Surface *selection;
75 /* gameloop funcs declerations */
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);
84 /* own declerations */
87 void le_loadlevel(void);
88 void le_change(int x, int y, int sx, unsigned char c);
90 void le_set_defaults(void);
91 void le_activate_bad_guys(void);
93 void le_activate_bad_guys(void)
97 /* Activate bad guys: */
99 /* as oposed to the gameloop.c func, this one doesn't remove
100 the badguys from tiles */
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');
108 void le_set_defaults()
112 if(current_level.time_left == 0)
113 current_level.time_left = 255;
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. */
121 /* FIXME: It should let select the user a level, which is in the leveldirectory and then load it. */
130 int x, y, i; /* for cicles */
131 int pos_x, cursor_x, cursor_y, old_cursor_x, fire;
135 int last_time, now_time;
137 strcpy(level_subset,"default");
142 menumenu = MENU_LEVELEDITOR;
145 frame = 0; /* support for frames in some tiles, like waves and bad guys */
148 loadlevel(¤t_level,"default",level);
151 le_activate_bad_guys();
154 selection = load_image(DATA_PREFIX "/images/leveleditor/select.png", USE_ALPHA);
159 old_cursor_x = cursor_x;
163 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
167 SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, current_level.bkgd_red, current_level.bkgd_green, current_level.bkgd_blue));
169 last_time = SDL_GetTicks();
172 keymod = SDL_GetModState();
174 while(SDL_PollEvent(&event))
176 // testing SDL_KEYDOWN, SDL_KEYUP and SDL_QUIT events
179 case SDL_KEYDOWN: // key pressed
180 key = event.key.keysym.sym;
190 cursor_x -= KEY_CURSOR_SPEED;
192 cursor_x -= KEY_CURSOR_FASTSPEED;
199 cursor_x += KEY_CURSOR_SPEED;
201 cursor_x += KEY_CURSOR_FASTSPEED;
203 if(cursor_x > (current_level.width*32) - 1)
204 cursor_x = (current_level.width*32) - 1;
208 cursor_y -= KEY_CURSOR_SPEED;
210 cursor_y -= KEY_CURSOR_FASTSPEED;
217 cursor_y += KEY_CURSOR_SPEED;
219 cursor_y += KEY_CURSOR_FASTSPEED;
221 if(cursor_y > 480-32)
234 cursor_x = (current_level.width * 32) - 32;
237 le_change(cursor_x, cursor_y, 0, '.');
240 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
241 le_change(cursor_x, cursor_y, 0, 'A');
243 le_change(cursor_x, cursor_y, 0, 'a');
246 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
247 le_change(cursor_x, cursor_y, 0, 'B');
250 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
251 le_change(cursor_x, cursor_y, 0, 'C');
253 le_change(cursor_x, cursor_y, 0, 'c');
256 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
257 le_change(cursor_x, cursor_y, 0, 'D');
259 le_change(cursor_x, cursor_y, 0, 'd');
262 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
263 le_change(cursor_x, cursor_y, 0, 'E');
265 le_change(cursor_x, cursor_y, 0, 'e');
268 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
269 le_change(cursor_x, cursor_y, 0, 'F');
271 le_change(cursor_x, cursor_y, 0, 'f');
274 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
275 le_change(cursor_x, cursor_y, 0, 'G');
277 le_change(cursor_x, cursor_y, 0, 'g');
280 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
281 le_change(cursor_x, cursor_y, 0, 'H');
283 le_change(cursor_x, cursor_y, 0, 'h');
286 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
287 le_change(cursor_x, cursor_y, 0, 'I');
289 le_change(cursor_x, cursor_y, 0, 'i');
292 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
293 le_change(cursor_x, cursor_y, 0, 'J');
295 le_change(cursor_x, cursor_y, 0, 'j');
298 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
299 le_change(cursor_x, cursor_y, 0, 'X');
301 le_change(cursor_x, cursor_y, 0, 'x');
304 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
305 le_change(cursor_x, cursor_y, 0, 'Y');
307 le_change(cursor_x, cursor_y, 0, 'y');
309 case SDLK_LEFTBRACKET:
310 le_change(cursor_x, cursor_y, 0, '[');
312 case SDLK_RIGHTBRACKET:
313 le_change(cursor_x, cursor_y, 0, ']');
317 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
318 le_change(cursor_x, cursor_y, 0, '#');
322 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
323 le_change(cursor_x, cursor_y, 0, '$');
326 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
327 le_change(cursor_x, cursor_y, 0, '|');
329 le_change(cursor_x, cursor_y, 0, '\\');
332 le_change(cursor_x, cursor_y, 0, '^');
336 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
337 le_change(cursor_x, cursor_y, 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');
346 for(i = 0; i < NUM_BAD_GUYS; ++i)
347 if (bad_guys[i].alive == NO)
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);
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');
362 for(i = 0; i < NUM_BAD_GUYS; ++i)
363 if (bad_guys[i].alive == NO)
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);
373 le_change(cursor_x, cursor_y, 0, '2');
375 for(i = 0; i < NUM_BAD_GUYS; ++i)
376 if (bad_guys[i].alive == NO)
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);
386 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
387 le_change(cursor_x, cursor_y, 0, '*');
393 case SDL_KEYUP: // key released
394 switch(event.key.keysym.sym)
409 case SDL_MOUSEBUTTONDOWN:
410 if(event.button.button == SDL_BUTTON_LEFT)
415 cursor_x = ((int)(pos_x + x) / 32) * 32;
416 cursor_y = ((int) y / 32) * 32;
419 case SDL_MOUSEMOTION:
423 cursor_x = ((int)(pos_x + x) / 32) * 32;
424 cursor_y = ((int) y / 32) * 32;
426 case SDL_QUIT: // window closed
434 /* mouse movements */
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;
441 if(old_cursor_x != cursor_x)
443 if(cursor_x < pos_x + KEY_LEFT_MARGIN)
444 pos_x = cursor_x - KEY_LEFT_MARGIN;
446 if(cursor_x > pos_x + KEY_RIGHT_MARGIN)
447 pos_x = cursor_x - KEY_RIGHT_MARGIN;
452 if(pos_x > (current_level.width * 32) - 640)
453 pos_x = (current_level.width * 32) - 640;
455 old_cursor_x = cursor_x;
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)]);
461 /* Draw the Bad guys: */
462 for (i = 0; i < NUM_BAD_GUYS; ++i)
464 /* printf("\nbad_guys[%i].alive = %i", i, bad_guys[i].alive); */
465 if(bad_guys[i].alive == NO)
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);
477 drawimage(selection, ((int)(cursor_x - pos_x)/32)*32, cursor_y, NO_UPDATE);
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);
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);
487 drawtext("F1 for Help", 10, 430, letters_blue, NO_UPDATE, 1);
495 if(done == DONE_QUIT)
499 now_time = SDL_GetTicks();
500 if (now_time < last_time + FPS)
501 SDL_Delay(last_time + FPS - now_time); /* delay some time */
509 SDL_FreeSurface(selection);
514 void le_change(int x, int y, int sx, unsigned char c)
520 xx = ((x + sx) / 32);
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;
529 if (yy >= 0 && yy < 15 && xx >= 0 && xx <= current_level.width)
530 current_level.tiles[yy][xx] = c;
533 /* Save data for this level: */
541 /* Save data file: */
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");
556 /* sptrinf("# Level created by SuperTux built-in editor", fi); */
558 fputs(current_level.name, fi);
560 fputs(current_level.theme, fi);
562 sprintf(str, "%d\n", current_level.time_left); /* time */
564 fputs(current_level.song_title, fi); /* song filename */
565 sprintf(str, "\n%d\n", current_level.bkgd_red); /* red background color */
567 sprintf(str, "%d\n", current_level.bkgd_green); /* green background color */
569 sprintf(str, "%d\n", current_level.bkgd_blue); /* blue background color */
571 sprintf(str, "%d\n", current_level.width); /* level width */
574 for(y = 0; y < 15; ++y)
576 fputs(current_level.tiles[y], fi);
582 drawcenteredtext("SAVED!", 240, letters_gold, NO_UPDATE, 1);
611 "./Del - Remove tile",
616 drawcenteredtext("- Help -", 30, letters_red, NO_UPDATE, 1);
617 drawtext("Keys:", 80, 60, letters_gold, NO_UPDATE, 1);
619 for(i = 0; i < sizeof(text)/sizeof(char *); i++)
620 drawtext(text[i], 40, 90+(i*16), letters_blue, NO_UPDATE, 1);
627 while(SDL_PollEvent(&event))
630 case SDL_KEYDOWN: // key pressed