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 - February 1st, 2004 */
13 /* leveleditor.c - A built-in level editor for SuperTux
14 by Ricardo Cruz <rick2@aeiou.pt> */
22 #include <SDL_image.h>
23 #include "leveleditor.h"
35 /* definitions to aid development */
36 #define DONE_LEVELEDITOR 1
38 #define DONE_CHANGELEVEL 3
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
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 */
51 #define MOUSE_LEFT_MARGIN 32
52 #define MOUSE_RIGHT_MARGIN 608
53 #define MOUSE_POS_SPEED 32
59 sprintf(str, "Editing Level %s", levelfilename);
60 drawcenteredtext(str, 200, letters_red, NO_UPDATE, 1);
62 sprintf(str, "%s", levelname);
63 drawcenteredtext(str, 224, letters_gold, NO_UPDATE, 1);
70 /* gameloop funcs declerations */
72 void loadshared(void);
73 void unloadshared(void);
75 /* own declerations */
78 void le_change(float x, float y, unsigned char c);
80 void le_set_defaults(void);
81 void le_activate_bad_guys(void);
83 /* global variables (based on the gameloop ones) */
86 st_level current_level;
87 char level_subset[100];
91 texture_type selection;
92 int last_time, now_time;
99 texture_free(&selection);
102 void le_activate_bad_guys(void)
106 /* Activate bad guys: */
108 /* as oposed to the gameloop.c func, this one doesn't remove
109 the badguys from tiles */
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');
117 void le_set_defaults()
121 if(current_level.time_left == 0)
122 current_level.time_left = 255;
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. */
129 /* FIXME: It should let select the user a level, which is in the leveldirectory and then load it. */
135 char str[LEVEL_NAME_MAX];
137 int x, y, i; /* for cicles */
138 int pos_x, cursor_x, cursor_y, cursor_tile, fire;
143 strcpy(level_subset,"default");
149 menumenu = MENU_LEVELEDITOR;
152 frame = 0; /* support for frames in some tiles, like waves and bad guys */
159 loadlevel(¤t_level,"default",level);
160 loadlevelgfx(¤t_level);
162 le_activate_bad_guys();
165 texture_load(&selection,DATA_PREFIX "/images/leveleditor/select.png", USE_ALPHA);
173 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
177 clearscreen(current_level.bkgd_red, current_level.bkgd_green, current_level.bkgd_blue);
179 last_time = SDL_GetTicks();
182 keymod = SDL_GetModState();
184 while(SDL_PollEvent(&event))
186 // testing SDL_KEYDOWN, SDL_KEYUP and SDL_QUIT events
189 case SDL_KEYDOWN: // key pressed
190 key = event.key.keysym.sym;
193 menu_event(&event.key.keysym);
200 cursor_x -= KEY_CURSOR_SPEED;
202 cursor_x -= KEY_CURSOR_FASTSPEED;
209 cursor_x += KEY_CURSOR_SPEED;
211 cursor_x += KEY_CURSOR_FASTSPEED;
213 if(cursor_x > (current_level.width*32) - 32)
214 cursor_x = (current_level.width*32) - 32;
218 cursor_y -= KEY_CURSOR_SPEED;
220 cursor_y -= KEY_CURSOR_FASTSPEED;
227 cursor_y += KEY_CURSOR_SPEED;
229 cursor_y += KEY_CURSOR_FASTSPEED;
231 if(cursor_y > screen->h-32)
232 cursor_y = screen->h-32;
244 cursor_x = (current_level.width * 32) - 32;
247 cursor_x -= PAGE_CURSOR_SPEED;
253 cursor_x += PAGE_CURSOR_SPEED;
255 if(cursor_x > (current_level.width*32) - 32)
256 cursor_x = (current_level.width*32) - 32;
265 le_change(cursor_x, cursor_y, '.');
268 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
269 le_change(cursor_x, cursor_y, 'A');
271 le_change(cursor_x, cursor_y, 'a');
274 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
275 le_change(cursor_x, cursor_y, 'B');
278 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
279 le_change(cursor_x, cursor_y, 'C');
281 le_change(cursor_x, cursor_y, 'c');
284 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
285 le_change(cursor_x, cursor_y, 'D');
287 le_change(cursor_x, cursor_y, 'd');
290 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
291 le_change(cursor_x, cursor_y, 'E');
293 le_change(cursor_x, cursor_y, 'e');
296 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
297 le_change(cursor_x, cursor_y, 'F');
299 le_change(cursor_x, cursor_y, 'f');
302 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
303 le_change(cursor_x, cursor_y, 'G');
305 le_change(cursor_x, cursor_y, 'g');
308 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
309 le_change(cursor_x, cursor_y, 'H');
311 le_change(cursor_x, cursor_y, 'h');
314 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
315 le_change(cursor_x, cursor_y, 'I');
317 le_change(cursor_x, cursor_y, 'i');
320 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
321 le_change(cursor_x, cursor_y, 'J');
323 le_change(cursor_x, cursor_y, 'j');
326 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
327 le_change(cursor_x, cursor_y, 'X');
329 le_change(cursor_x, cursor_y, 'x');
332 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
333 le_change(cursor_x, cursor_y, 'Y');
335 le_change(cursor_x, cursor_y, 'y');
337 case SDLK_LEFTBRACKET:
338 le_change(cursor_x, cursor_y, '[');
340 case SDLK_RIGHTBRACKET:
341 le_change(cursor_x, cursor_y, ']');
345 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
346 le_change(cursor_x, cursor_y, '#');
350 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
351 le_change(cursor_x, cursor_y, '$');
354 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
355 le_change(cursor_x, cursor_y, '|');
357 le_change(cursor_x, cursor_y, '\\');
360 le_change(cursor_x, cursor_y, '^');
364 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
365 le_change(cursor_x, cursor_y, '&');
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');
374 add_bad_guy((((int)cursor_x/32)*32), (((int)cursor_y/32)*32), BAD_BSOD);
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');
382 add_bad_guy((((int)cursor_x/32)*32), (((int)cursor_y/32)*32), BAD_LAPTOP);
385 le_change(cursor_x, cursor_y, '2');
387 add_bad_guy((((int)cursor_x/32)*32), (((int)cursor_y/32)*32), BAD_MONEY);
390 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
391 le_change(cursor_x, cursor_y, '*');
397 case SDL_KEYUP: // key released
398 switch(event.key.keysym.sym)
413 /* case SDL_MOUSEBUTTONDOWN:
414 if(event.button.button == SDL_BUTTON_LEFT)
416 This will draw current tile in the cursor position, when the interface is done.
419 case SDL_MOUSEMOTION:
425 cursor_x = ((int)(pos_x + x) / 32) * 32;
426 cursor_y = ((int) y / 32) * 32;
429 case SDL_QUIT: // window closed
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;*/
445 if(cursor_x < pos_x + CURSOR_LEFT_MARGIN)
446 pos_x = cursor_x - CURSOR_LEFT_MARGIN;
448 if(cursor_x > pos_x + CURSOR_RIGHT_MARGIN)
449 pos_x = cursor_x - CURSOR_RIGHT_MARGIN;
453 if(pos_x > (current_level.width * 32) - screen->w)
454 pos_x = (current_level.width * 32) - screen->w;
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)]);
460 /* draw whats inside stuff when cursor is selecting those */
461 cursor_tile = current_level.tiles[cursor_y/32][cursor_x/32];
465 texture_draw(&img_mints, cursor_x - pos_x, cursor_y, NO_UPDATE);
468 texture_draw(&img_golden_herring, cursor_x - pos_x, cursor_y, NO_UPDATE);
473 texture_draw(&img_distro[(frame / 5) % 4], cursor_x - pos_x, cursor_y, NO_UPDATE);
479 /* Draw the Bad guys: */
480 for (i = 0; i < num_bad_guys; ++i)
482 if(bad_guys[i].base.alive == NO)
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);
493 /* draw a grid (if selected) */
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);
502 texture_draw(&selection, ((int)(cursor_x - pos_x)/32)*32, cursor_y, NO_UPDATE);
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);
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);
512 text_draw(&blue_text, "F1 for Help", 10, 430, 1, NO_UPDATE);
523 if(done == DONE_QUIT)
530 now_time = SDL_GetTicks();
531 if (now_time < last_time + FPS)
532 SDL_Delay(last_time + FPS - now_time); /* delay some time */
540 void le_change(float x, float y, unsigned char c)
545 level_change(¤t_level,x,y,c);
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;
557 /* Save data for this level: */
565 /* Save data file: */
567 filename = (char *) malloc(sizeof(char) * (strlen(DATA_PREFIX) + 20) + strlen(level_subset));
568 sprintf(filename, "%s/levels/%s/level%d.dat", DATA_PREFIX, level_subset, level);
569 fi = fopen(filename, "w");
580 /* sptrinf("# Level created by SuperTux built-in editor", fi); */
582 fputs(current_level.name, fi);
584 fputs(current_level.theme, fi);
586 sprintf(str, "%d\n", current_level.time_left); /* time */
588 fputs(current_level.song_title, fi); /* song filename */
590 fputs(current_level.bkgd_image, fi); /* background image */
591 sprintf(str, "\n%d\n", current_level.bkgd_red); /* red background color */
593 sprintf(str, "%d\n", current_level.bkgd_green); /* green background color */
595 sprintf(str, "%d\n", current_level.bkgd_blue); /* blue background color */
597 sprintf(str, "%d\n", current_level.width); /* level width */
600 for(y = 0; y < 15; ++y)
602 fputs(current_level.tiles[y], fi);
608 text_drawf(&gold_text, "SAVED!", 0, 240, A_HMIDDLE, A_TOP, 1, NO_UPDATE);
637 "./Del - Remove tile",
638 "F9 - Show/Hide Grid",
642 text_drawf(&red_text, "- Help -", 0, 30, A_HMIDDLE, A_TOP, 2, NO_UPDATE);
643 text_draw(&gold_text, "Keys:", 80, 60, 1, NO_UPDATE);
645 for(i = 0; i < sizeof(text)/sizeof(char *); i++)
646 text_draw(&blue_text, text[i], 40, 90+(i*16), 1, NO_UPDATE);
648 text_drawf(&gold_text, "Press Any Key to Continue", 0, 460, A_HMIDDLE, A_TOP, 1, NO_UPDATE);
655 while(SDL_PollEvent(&event))
658 case SDL_MOUSEBUTTONDOWN: // mouse pressed
659 case SDL_KEYDOWN: // key pressed