1 /***************************************************************************
3 * This program is free software; you can redistribute it and/or modify *
4 * it under the terms of the GNU General Public License as published by *
5 * the Free Software Foundation; either version 2 of the License, or *
6 * (at your option) any later version. *
8 ***************************************************************************/
10 /* December 28, 2003 - February 1st, 2004 */
12 /* leveleditor.c - A built-in level editor for SuperTux
13 by Ricardo Cruz <rick2@aeiou.pt> */
21 #include <SDL_image.h>
22 #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
43 /* when pagedown/up pressed speed:*/
44 #define PAGE_CURSOR_SPEED 13*32
46 #define MOUSE_LEFT_MARGIN 80
47 #define MOUSE_RIGHT_MARGIN (560-32)
48 /* right_margin should noticed that the cursor is 32 pixels,
49 so it should subtract that value */
50 #define MOUSE_POS_SPEED 20
53 #define SELECT_W 2 // size of the selections lines
54 #define SELECT_CLR 0, 255, 0, 255 // lines color (R, G, B, A)
56 /* gameloop funcs declerations */
58 void loadshared(void);
59 void unloadshared(void);
61 /* own declerations */
62 /* crutial ones (main loop) */
66 void le_checkevents();
67 void le_change(float x, float y, unsigned char c);
70 void le_set_defaults(void);
71 void le_activate_bad_guys(void);
73 void le_highlight_selection();
75 /* leveleditor internals */
76 static int le_level_changed; /* if changes, ask for saving, when quiting*/
77 static int pos_x, cursor_x, cursor_y, cursor_tile, fire;
79 static st_level le_current_level;
80 static st_subset le_level_subset;
81 static int le_show_grid;
83 static texture_type le_selection;
85 static char le_current_tile;
86 static int le_mouse_pressed;
87 static button_type le_test_level_bt;
88 static button_type le_next_level_bt;
89 static button_type le_previous_level_bt;
90 static button_type le_rubber_bt;
91 static button_type le_select_mode_one_bt;
92 static button_type le_select_mode_two_bt;
93 static button_type le_bad_bsod_bt;
94 static button_panel_type le_bt_panel;
96 static square selection;
97 static int le_selection_mode;
98 static SDL_Event event;
100 void le_activate_bad_guys(void)
104 /* Activate bad guys: */
106 /* as oposed to the gameloop.c func, this one doesn't remove
107 the badguys from tiles */
109 for (y = 0; y < 15; ++y)
110 for (x = 0; x < le_current_level.width; ++x)
111 if (le_current_level.tiles[y][x] >= '0' && le_current_level.tiles[y][x] <= '9')
112 add_bad_guy(x * 32, y * 32, le_current_level.tiles[y][x] - '0');
115 void le_set_defaults()
119 if(le_current_level.time_left == 0)
120 le_current_level.time_left = 255;
123 /* 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. */
127 /* FIXME: It should let select the user a level, which is in the leveldirectory and then load it. */
131 int leveleditor(int levelnb)
133 int last_time, now_time;
141 last_time = SDL_GetTicks();
146 /* making events results to be in order */
149 if(pos_x > (le_current_level.width * 32) - screen->w)
150 pos_x = (le_current_level.width * 32) - screen->w;
157 menu_process_current();
158 if(current_menu == &leveleditor_menu)
160 switch (menu_check(&leveleditor_menu))
166 done = DONE_LEVELEDITOR;
178 if(done == DONE_QUIT)
185 now_time = SDL_GetTicks();
186 if (now_time < last_time + FPS)
187 SDL_Delay(last_time + FPS - now_time); /* delay some time */
197 subset_load(&le_level_subset,"default");
200 /* level_changed = NO;*/
204 menu_set_current(&leveleditor_menu);
205 le_frame = 0; /* support for frames in some tiles, like waves and bad guys */
211 le_level_changed = NO;
212 if(level_load(&le_current_level, le_level_subset.name, le_level) != 0)
217 if(le_current_level.time_left == 0)
218 le_current_level.time_left = 255;
220 level_load_gfx(&le_current_level);
222 le_current_tile = '.';
223 le_mouse_pressed = NO;
224 le_activate_bad_guys();
226 texture_load(&le_selection,DATA_PREFIX "/images/leveleditor/select.png", USE_ALPHA);
229 button_load(&le_test_level_bt,"/images/icons/test-level.png","Test Level",SDLK_F4,150,screen->h - 64);
230 button_load(&le_next_level_bt,"/images/icons/up.png","Test Level", SDLK_PAGEUP,screen->w-64,0);
231 button_load(&le_previous_level_bt,"/images/icons/down.png","Test Level",SDLK_PAGEDOWN,screen->w-32,0);
232 button_load(&le_rubber_bt,"/images/icons/rubber.png","Rubber",SDLK_DELETE,screen->w-64,32);
233 button_load(&le_select_mode_one_bt,"/images/icons/select-mode1.png","Select Tile",SDLK_F3,screen->w-64,16);
234 button_load(&le_select_mode_two_bt,"/images/icons/select-mode2.png","Select Tiles",SDLK_F3,screen->w-32,16);
235 button_load(&le_bad_bsod_bt,"/images/shared/bsod-left-1.png","Select Tiles",'0',screen->w-32,32);
236 button_panel_init(&le_bt_panel, 500,100, 64, 400);
237 button_panel_additem(&le_bt_panel, button_create("/images/shared/bsod-left-1.png","Select Tiles",'0',screen->w-32,32));
239 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
244 void le_goto_level(int levelnb)
247 level_free(&le_current_level);
248 if(level_load(&le_current_level, le_level_subset.name, levelnb) != 0)
250 level_load(&le_current_level, le_level_subset.name, le_level);
256 if(le_current_level.time_left == 0)
257 le_current_level.time_left = 255;
260 level_load_gfx(&le_current_level);
262 le_activate_bad_guys();
267 /*if(level_changed == YES)
268 if(askforsaving() == CANCEL)
271 SDL_EnableKeyRepeat(0, 0); // disables key repeating
273 button_free(&le_test_level_bt);
275 level_free(&le_current_level);
278 texture_free(&le_selection);
284 static char str[LEVEL_NAME_MAX];
286 /* Draw the real background */
287 if(le_current_level.bkgd_image[0] != '\0')
290 texture_draw_part(&img_bkgd,s,0,0,0,img_bkgd.w - s - 32, img_bkgd.h, NO_UPDATE);
291 texture_draw_part(&img_bkgd,0,0,screen->w - s - 32 ,0,s,img_bkgd.h, NO_UPDATE);
295 clearscreen(le_current_level.bkgd_red, le_current_level.bkgd_green, le_current_level.bkgd_blue);
298 /* clearscreen(current_level.bkgd_red, current_level.bkgd_green, current_level.bkgd_blue); */
300 for (y = 0; y < 15; ++y)
301 for (x = 0; x < 20; ++x)
303 drawshape(x * 32 - ((int)pos_x % 32), y * 32, le_current_level.tiles[y][x + (int)(pos_x / 32)]);
305 /* draw whats inside stuff when cursor is selecting those */
306 /* (draw them all the time - is this the right behaviour?) */
307 switch(le_current_level.tiles[y][x + (int)(pos_x/32)])
310 texture_draw(&img_mints, x * 32 - ((int)pos_x % 32), y*32, NO_UPDATE);
313 texture_draw(&img_golden_herring, x * 32 - ((int)pos_x % 32), y*32, NO_UPDATE);
318 texture_draw(&img_distro[(frame / 5) % 4], x * 32 - ((int)pos_x % 32), y*32, NO_UPDATE);
325 /* Draw the Bad guys: */
326 for (i = 0; i < num_bad_guys; ++i)
328 if(bad_guys[i].base.alive == NO)
330 /* to support frames: img_bsod_left[(frame / 5) % 4] */
331 if(bad_guys[i].kind == BAD_BSOD)
332 texture_draw(&img_bsod_left[(le_frame / 5) % 4], bad_guys[i].base.x - pos_x, bad_guys[i].base.y, NO_UPDATE);
333 else if(bad_guys[i].kind == BAD_LAPTOP)
334 texture_draw(&img_laptop_left[(le_frame / 5) % 3], bad_guys[i].base.x - pos_x, bad_guys[i].base.y, NO_UPDATE);
335 else if (bad_guys[i].kind == BAD_MONEY)
336 texture_draw(&img_money_left[(le_frame / 5) % 2], bad_guys[i].base.x - pos_x, bad_guys[i].base.y, NO_UPDATE);
339 /* Draw the player: */
340 /* for now, the position is fixed at (0, 240) */
341 texture_draw(&tux_right[(frame / 5) % 3], 0 - pos_x, 240, NO_UPDATE);
343 /* draw a grid (if selected) */
346 for(x = 0; x < 19; x++)
347 fillrect(x*32 - ((int)pos_x % 32), 0, 1, screen->h, 225, 225, 225,255);
348 for(y = 0; y < 15; y++)
349 fillrect(0, y*32, screen->w - 32, 1, 225, 225, 225,255);
352 if(le_selection_mode == CURSOR)
353 texture_draw(&le_selection, cursor_x - pos_x, cursor_y, NO_UPDATE);
354 else if(le_selection_mode == SQUARE)
357 le_highlight_selection();
358 /* draw current selection */
359 w = selection.x2 - selection.x1;
360 h = selection.y2 - selection.y1;
361 fillrect(selection.x1 - pos_x, selection.y1, w, SELECT_W, SELECT_CLR);
362 fillrect(selection.x1 - pos_x + w, selection.y1, SELECT_W, h, SELECT_CLR);
363 fillrect(selection.x1 - pos_x, selection.y1 + h, w, SELECT_W, SELECT_CLR);
364 fillrect(selection.x1 - pos_x, selection.y1, SELECT_W, h, SELECT_CLR);
368 /* draw button bar */
369 fillrect(screen->w - 64, 0, 64, screen->h, 50, 50, 50,255);
370 drawshape(19 * 32, 14 * 32, le_current_tile);
372 button_draw(&le_test_level_bt);
373 button_draw(&le_next_level_bt);
374 button_draw(&le_previous_level_bt);
375 button_draw(&le_rubber_bt);
376 button_draw(&le_select_mode_one_bt);
377 button_draw(&le_select_mode_two_bt);
378 button_draw(&le_bad_bsod_bt);
379 button_panel_draw(&le_bt_panel);
381 sprintf(str, "%d", le_current_level.time_left);
382 text_draw(&white_text, "TIME", 324, 0, 1, NO_UPDATE);
383 text_draw(&gold_text, str, 404, 0, 1, NO_UPDATE);
385 text_draw(&white_text, "NAME", 0, 0, 1, NO_UPDATE);
386 text_draw(&gold_text, le_current_level.name, 80, 0, 1, NO_UPDATE);
388 sprintf(str, "%d/%d", le_level,le_level_subset.levels);
389 text_draw(&white_text, "NUMB", 0, 20, 1, NO_UPDATE);
390 text_draw(&gold_text, str, 80, 20, 1, NO_UPDATE);
392 text_draw(&white_small_text, "F1 for Help", 10, 430, 1, NO_UPDATE);
393 text_draw(&white_small_text, "F2 for Testing", 150, 430, 1, NO_UPDATE);
396 void le_checkevents()
402 keymod = SDL_GetModState();
404 while(SDL_PollEvent(&event))
406 /* testing SDL_KEYDOWN, SDL_KEYUP and SDL_QUIT events*/
407 if(event.type == SDL_KEYDOWN || ((event.type == SDL_MOUSEBUTTONDOWN || SDL_MOUSEMOTION) && (event.motion.x > 0 && event.motion.x < screen->w - 64 &&
408 event.motion.y > 0 && event.motion.y < screen->h)))
413 case SDL_KEYDOWN: // key pressed
414 key = event.key.keysym.sym;
417 menu_event(&event.key.keysym);
430 cursor_x -= KEY_CURSOR_SPEED;
432 cursor_x -= KEY_CURSOR_FASTSPEED;
434 if(cursor_x < pos_x + MOUSE_LEFT_MARGIN)
435 pos_x = cursor_x - MOUSE_LEFT_MARGIN;
440 cursor_x += KEY_CURSOR_SPEED;
442 cursor_x += KEY_CURSOR_FASTSPEED;
444 if(cursor_x > pos_x + MOUSE_RIGHT_MARGIN-32)
445 pos_x = cursor_x - MOUSE_RIGHT_MARGIN+32;
450 cursor_y -= KEY_CURSOR_SPEED;
452 cursor_y -= KEY_CURSOR_FASTSPEED;
459 cursor_y += KEY_CURSOR_SPEED;
461 cursor_y += KEY_CURSOR_FASTSPEED;
463 if(cursor_y > screen->h-32)
464 cursor_y = screen->h-32;
477 cursor_x = (le_current_level.width * 32) - 32;
481 cursor_x -= PAGE_CURSOR_SPEED;
483 if(cursor_x < pos_x + MOUSE_LEFT_MARGIN)
484 pos_x = cursor_x - MOUSE_LEFT_MARGIN;
488 cursor_x += PAGE_CURSOR_SPEED;
490 if(cursor_x > pos_x + MOUSE_RIGHT_MARGIN-32)
491 pos_x = cursor_x - MOUSE_RIGHT_MARGIN+32;
495 le_show_grid = !le_show_grid;
498 le_change(cursor_x, cursor_y, '.');
501 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
502 le_current_tile = 'A';
504 le_current_tile = 'a';
507 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
508 le_change(cursor_x, cursor_y, 'B');
511 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
512 le_change(cursor_x, cursor_y, 'C');
514 le_change(cursor_x, cursor_y, 'c');
517 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
518 le_change(cursor_x, cursor_y, 'D');
520 le_change(cursor_x, cursor_y, 'd');
523 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
524 le_change(cursor_x, cursor_y, 'E');
526 le_change(cursor_x, cursor_y, 'e');
529 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
530 le_change(cursor_x, cursor_y, 'F');
532 le_change(cursor_x, cursor_y, 'f');
535 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
536 le_change(cursor_x, cursor_y, 'G');
538 le_change(cursor_x, cursor_y, 'g');
541 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
542 le_change(cursor_x, cursor_y, 'H');
544 le_change(cursor_x, cursor_y, 'h');
547 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
548 le_change(cursor_x, cursor_y, 'I');
550 le_change(cursor_x, cursor_y, 'i');
553 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
554 le_change(cursor_x, cursor_y, 'J');
556 le_change(cursor_x, cursor_y, 'j');
559 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
560 le_change(cursor_x, cursor_y, 'X');
562 le_change(cursor_x, cursor_y, 'x');
565 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
566 le_change(cursor_x, cursor_y, 'Y');
568 le_change(cursor_x, cursor_y, 'y');
570 case SDLK_LEFTBRACKET:
571 le_change(cursor_x, cursor_y, '[');
573 case SDLK_RIGHTBRACKET:
574 le_change(cursor_x, cursor_y, ']');
578 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
579 le_change(cursor_x, cursor_y, '#');
583 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
584 le_change(cursor_x, cursor_y, '$');
587 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
588 le_change(cursor_x, cursor_y, '|');
590 le_change(cursor_x, cursor_y, '\\');
593 le_change(cursor_x, cursor_y, '^');
597 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
598 le_change(cursor_x, cursor_y, '&');
602 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
603 le_change(cursor_x, cursor_y, '=');
604 else /* let's add a bad guy */
605 le_change(cursor_x, cursor_y, '0');
608 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
609 le_change(cursor_x, cursor_y, '!');
610 else /* let's add a bad guy */
611 le_change(cursor_x, cursor_y, '1');
614 le_change(cursor_x, cursor_y, '2');
617 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
618 le_change(cursor_x, cursor_y, '*');
624 case SDL_KEYUP: // key released
625 switch(event.key.keysym.sym)
634 case SDL_MOUSEBUTTONDOWN:
635 if(event.button.button == SDL_BUTTON_LEFT)
637 le_mouse_pressed = YES;
639 selection.x1 = event.motion.x + pos_x;
640 selection.y1 = event.motion.y;
641 selection.x2 = event.motion.x + pos_x;
642 selection.y2 = event.motion.y;
645 case SDL_MOUSEBUTTONUP:
646 if(event.button.button == SDL_BUTTON_LEFT)
648 le_mouse_pressed = NO;
651 case SDL_MOUSEMOTION:
657 cursor_x = ((int)(pos_x + x) / 32) * 32;
658 cursor_y = ((int) y / 32) * 32;
660 if(le_mouse_pressed == YES)
662 selection.x2 = x + pos_x;
667 case SDL_QUIT: // window closed
679 if(event.type == SDL_KEYDOWN || ((event.type == SDL_MOUSEBUTTONDOWN || SDL_MOUSEMOTION) && (event.motion.x > screen->w-64 && event.motion.x < screen->w &&
680 event.motion.y > 0 && event.motion.y < screen->h)))
682 /* Check for button events */
683 button_event(&le_test_level_bt,&event);
684 if(button_get_state(&le_test_level_bt) == BN_CLICKED)
686 button_event(&le_next_level_bt,&event);
687 if(button_get_state(&le_next_level_bt) == BN_CLICKED)
689 if(le_level < le_level_subset.levels)
690 le_goto_level(++le_level);
692 button_event(&le_previous_level_bt,&event);
693 if(button_get_state(&le_previous_level_bt) == BN_CLICKED)
696 le_goto_level(--le_level);
698 button_event(&le_rubber_bt,&event);
699 if(button_get_state(&le_rubber_bt) == BN_CLICKED)
700 le_current_tile = '.';
701 button_event(&le_select_mode_one_bt,&event);
702 if(button_get_state(&le_select_mode_one_bt) == BN_CLICKED)
703 le_selection_mode = CURSOR;
704 button_event(&le_select_mode_two_bt,&event);
705 if(button_get_state(&le_select_mode_two_bt) == BN_CLICKED)
706 le_selection_mode = SQUARE;
707 button_event(&le_bad_bsod_bt,&event);
708 if(button_get_state(&le_bad_bsod_bt) == BN_CLICKED)
709 le_current_tile = '0';
714 le_change(cursor_x, cursor_y, le_current_tile);
719 void le_highlight_selection()
724 if(selection.x1 < selection.x2)
734 if(selection.y1 < selection.y2)
750 fillrect(x1*32-pos_x, y1*32,32* (x2 - x1 + 1),32 * (y2 - y1 + 1),173,234,177,103);
753 void le_change(float x, float y, unsigned char c)
758 /* level_changed = YES; */
760 switch(le_selection_mode)
763 level_change(&le_current_level,x,y,c);
768 /* if there is a bad guy over there, remove it */
769 for(i = 0; i < num_bad_guys; ++i)
770 if (bad_guys[i].base.alive)
771 if(xx == bad_guys[i].base.x/32 && yy == bad_guys[i].base.y/32)
772 bad_guys[i].base.alive = NO;
774 if(c == '0') /* if it's a bad guy */
775 add_bad_guy(xx*32, yy*32, BAD_BSOD);
777 add_bad_guy(xx*32, yy*32, BAD_LAPTOP);
779 add_bad_guy(xx*32, yy*32, BAD_MONEY);
783 if(selection.x1 < selection.x2)
793 if(selection.y1 < selection.y2)
809 /* if there is a bad guy over there, remove it */
810 for(i = 0; i < num_bad_guys; ++i)
811 if(bad_guys[i].base.alive)
812 if(bad_guys[i].base.x/32 >= x1 && bad_guys[i].base.x/32 <= x2
813 && bad_guys[i].base.y/32 >= y1 && bad_guys[i].base.y/32 <= y2)
814 bad_guys[i].base.alive = NO;
816 for(xx = x1; xx <= x2; xx++)
817 for(yy = y1; yy <= y2; yy++)
819 level_change(&le_current_level, xx*32, yy*32, c);
821 if(c == '0') // if it's a bad guy
822 add_bad_guy(xx*32, yy*32, BAD_BSOD);
824 add_bad_guy(xx*32, yy*32, BAD_LAPTOP);
826 add_bad_guy(xx*32, yy*32, BAD_MONEY);
836 level_save(&le_current_level,"test",le_level);
837 gameloop("test",le_level, ST_GL_TEST);
838 menu_set_current(&leveleditor_menu);
840 level_load_gfx(&le_current_level);
842 le_activate_bad_guys();
869 "./Del - Remove tile",
870 "F9 - Show/Hide Grid",
871 "F3 - Change Selection Mode",
875 text_drawf(&blue_text, "- Help -", 0, 30, A_HMIDDLE, A_TOP, 2, NO_UPDATE);
876 text_draw(&gold_text, "Keys:", 80, 60, 1, NO_UPDATE);
878 for(i = 0; i < sizeof(text)/sizeof(char *); i++)
879 text_draw(&white_text, text[i], 40, 90+(i*16), 1, NO_UPDATE);
881 text_drawf(&gold_text, "Press Any Key to Continue", 0, 460, A_HMIDDLE, A_TOP, 1, NO_UPDATE);
888 while(SDL_PollEvent(&event))
891 case SDL_MOUSEBUTTONDOWN: // mouse pressed
892 case SDL_KEYDOWN: // key pressed