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 - January 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
44 #define CURSOR_LEFT_MARGIN 96
45 #define CURSOR_RIGHT_MARGIN 512
46 /* right_margin should noticed that the cursor is 32 pixels,
47 so it should subtract that value */
49 #define MOUSE_LEFT_MARGIN 32
50 #define MOUSE_RIGHT_MARGIN 608
51 #define MOUSE_POS_SPEED 32
57 sprintf(str, "Editing Level %s", levelfilename);
58 drawcenteredtext(str, 200, letters_red, NO_UPDATE, 1);
60 sprintf(str, "%s", levelname);
61 drawcenteredtext(str, 224, letters_gold, NO_UPDATE, 1);
68 /* gameloop funcs declerations */
70 void loadshared(void);
71 void unloadshared(void);
73 /* own declerations */
76 void le_change(float x, float y, unsigned char c);
78 void le_set_defaults(void);
79 void le_activate_bad_guys(void);
81 /* global variables (based on the gameloop ones) */
84 st_level current_level;
85 char level_subset[100];
88 texture_type selection;
89 int last_time, now_time;
91 void le_activate_bad_guys(void)
95 /* Activate bad guys: */
97 /* as oposed to the gameloop.c func, this one doesn't remove
98 the badguys from tiles */
100 for (y = 0; y < 15; ++y)
101 for (x = 0; x < current_level.width; ++x)
102 if (current_level.tiles[y][x] >= '0' && current_level.tiles[y][x] <= '9')
103 add_bad_guy(x * 32, y * 32, current_level.tiles[y][x] - '0');
106 void le_set_defaults()
110 if(current_level.time_left == 0)
111 current_level.time_left = 255;
114 /* 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. */
118 /* FIXME: It should let select the user a level, which is in the leveldirectory and then load it. */
124 char str[LEVEL_NAME_MAX];
126 int x, y, i; /* for cicles */
127 int pos_x, cursor_x, cursor_y, fire;
132 strcpy(level_subset,"default");
137 menumenu = MENU_LEVELEDITOR;
140 frame = 0; /* support for frames in some tiles, like waves and bad guys */
145 loadlevel(¤t_level,"default",level);
146 loadlevelgfx(¤t_level);
148 le_activate_bad_guys();
151 texture_load(&selection,DATA_PREFIX "/images/leveleditor/select.png", USE_ALPHA);
159 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
163 clearscreen(current_level.bkgd_red, current_level.bkgd_green, current_level.bkgd_blue);
165 last_time = SDL_GetTicks();
168 keymod = SDL_GetModState();
170 while(SDL_PollEvent(&event))
172 // testing SDL_KEYDOWN, SDL_KEYUP and SDL_QUIT events
175 case SDL_KEYDOWN: // key pressed
176 key = event.key.keysym.sym;
186 cursor_x -= KEY_CURSOR_SPEED;
188 cursor_x -= KEY_CURSOR_FASTSPEED;
195 cursor_x += KEY_CURSOR_SPEED;
197 cursor_x += KEY_CURSOR_FASTSPEED;
199 if(cursor_x > (current_level.width*32) - 1)
200 cursor_x = (current_level.width*32) - 1;
204 cursor_y -= KEY_CURSOR_SPEED;
206 cursor_y -= KEY_CURSOR_FASTSPEED;
213 cursor_y += KEY_CURSOR_SPEED;
215 cursor_y += KEY_CURSOR_FASTSPEED;
217 if(cursor_y > screen->h-32)
218 cursor_y = screen->h-32;
230 cursor_x = (current_level.width * 32) - 32;
233 le_change(cursor_x, cursor_y, '.');
236 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
237 le_change(cursor_x, cursor_y, 'A');
239 le_change(cursor_x, cursor_y, 'a');
242 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
243 le_change(cursor_x, cursor_y, 'B');
246 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
247 le_change(cursor_x, cursor_y, 'C');
249 le_change(cursor_x, cursor_y, 'c');
252 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
253 le_change(cursor_x, cursor_y, 'D');
255 le_change(cursor_x, cursor_y, 'd');
258 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
259 le_change(cursor_x, cursor_y, 'E');
261 le_change(cursor_x, cursor_y, 'e');
264 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
265 le_change(cursor_x, cursor_y, 'F');
267 le_change(cursor_x, cursor_y, 'f');
270 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
271 le_change(cursor_x, cursor_y, 'G');
273 le_change(cursor_x, cursor_y, 'g');
276 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
277 le_change(cursor_x, cursor_y, 'H');
279 le_change(cursor_x, cursor_y, 'h');
282 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
283 le_change(cursor_x, cursor_y, 'I');
285 le_change(cursor_x, cursor_y, 'i');
288 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
289 le_change(cursor_x, cursor_y, 'J');
291 le_change(cursor_x, cursor_y, 'j');
294 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
295 le_change(cursor_x, cursor_y, 'X');
297 le_change(cursor_x, cursor_y, 'x');
300 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
301 le_change(cursor_x, cursor_y, 'Y');
303 le_change(cursor_x, cursor_y, 'y');
305 case SDLK_LEFTBRACKET:
306 le_change(cursor_x, cursor_y, '[');
308 case SDLK_RIGHTBRACKET:
309 le_change(cursor_x, cursor_y, ']');
313 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
314 le_change(cursor_x, cursor_y, '#');
318 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
319 le_change(cursor_x, cursor_y, '$');
322 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
323 le_change(cursor_x, cursor_y, '|');
325 le_change(cursor_x, cursor_y, '\\');
328 le_change(cursor_x, cursor_y, '^');
332 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
333 le_change(cursor_x, cursor_y, '&');
337 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
338 le_change(cursor_x, cursor_y, '=');
339 else /* let's add a bad guy */
340 le_change(cursor_x, cursor_y, '0');
342 for(i = 0; i < NUM_BAD_GUYS; ++i)
343 if (bad_guys[i].alive == NO)
345 bad_guys[i].alive = YES;
346 bad_guys[i].kind = BAD_BSOD;
347 bad_guys[i].x = (((int)cursor_x/32)*32);
348 bad_guys[i].y = (((int)cursor_y/32)*32);
353 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
354 le_change(cursor_x, cursor_y, '!');
355 else /* let's add a bad guy */
356 le_change(cursor_x, cursor_y, '1');
358 for(i = 0; i < NUM_BAD_GUYS; ++i)
359 if (bad_guys[i].alive == NO)
361 bad_guys[i].alive = YES;
362 bad_guys[i].kind = BAD_LAPTOP;
363 bad_guys[i].x = (((int)cursor_x/32)*32);
364 bad_guys[i].y = (((int)cursor_y/32)*32);
369 le_change(cursor_x, cursor_y, '2');
371 for(i = 0; i < NUM_BAD_GUYS; ++i)
372 if (bad_guys[i].alive == NO)
374 bad_guys[i].alive = YES;
375 bad_guys[i].kind = BAD_MONEY;
376 bad_guys[i].x = (((int)cursor_x/32)*32);
377 bad_guys[i].y = (((int)cursor_y/32)*32);
382 if(keymod == KMOD_LSHIFT || keymod == KMOD_RSHIFT || keymod == KMOD_CAPS)
383 le_change(cursor_x, cursor_y, '*');
389 case SDL_KEYUP: // key released
390 switch(event.key.keysym.sym)
405 /* case SDL_MOUSEBUTTONDOWN:
406 if(event.button.button == SDL_BUTTON_LEFT)
408 This will draw current tile in the cursor position, when the interface is done.
411 case SDL_MOUSEMOTION:
417 cursor_x = ((int)(pos_x + x) / 32) * 32;
418 cursor_y = ((int) y / 32) * 32;
421 case SDL_QUIT: // window closed
429 /* mouse movements */
430 /* x = event.motion.x;
431 if(x < MOUSE_LEFT_MARGIN)
432 pos_x -= MOUSE_POS_SPEED;
433 else if(x > MOUSE_RIGHT_MARGIN)
434 pos_x += MOUSE_POS_SPEED;*/
437 if(cursor_x < pos_x + CURSOR_LEFT_MARGIN)
438 pos_x = cursor_x - CURSOR_LEFT_MARGIN;
440 if(cursor_x > pos_x + CURSOR_RIGHT_MARGIN)
441 pos_x = cursor_x - CURSOR_RIGHT_MARGIN;
445 if(pos_x > (current_level.width * 32) - screen->w)
446 pos_x = (current_level.width * 32) - screen->w;
448 for (y = 0; y < 15; ++y)
449 for (x = 0; x < 21; ++x)
450 drawshape(x * 32, y * 32, current_level.tiles[y][x + (pos_x / 32)]);
452 /* Draw the Bad guys: */
453 for (i = 0; i < NUM_BAD_GUYS; ++i)
455 /* printf("\nbad_guys[%i].alive = %i", i, bad_guys[i].alive); */
456 if(bad_guys[i].alive == NO)
458 /* to support frames: img_bsod_left[(frame / 5) % 4] */
459 if(bad_guys[i].kind == BAD_BSOD)
460 texture_draw(&img_bsod_left[(frame / 5) % 4], ((int)(bad_guys[i].x - pos_x)/32)*32, bad_guys[i].y, NO_UPDATE);
461 else if(bad_guys[i].kind == BAD_LAPTOP)
462 texture_draw(&img_laptop_left[(frame / 5) % 3], ((int)(bad_guys[i].x - pos_x)/32)*32, bad_guys[i].y, NO_UPDATE);
463 else if (bad_guys[i].kind == BAD_MONEY)
464 texture_draw(&img_money_left[(frame / 5) % 2], ((int)(bad_guys[i].x - pos_x)/32)*32, bad_guys[i].y, NO_UPDATE);
468 texture_draw(&selection, ((int)(cursor_x - pos_x)/32)*32, cursor_y, NO_UPDATE);
470 sprintf(str, "%d", current_level.time_left);
471 drawtext("TIME", 324, 0, letters_blue, NO_UPDATE, 1);
472 drawtext(str, 404, 0, letters_gold, NO_UPDATE, 1);
474 sprintf(str, "%s", current_level.name);
475 drawtext("NAME", 0, 0, letters_blue, NO_UPDATE, 1);
476 drawtext(str, 80, 0, letters_gold, NO_UPDATE, 1);
478 drawtext("F1 for Help", 10, 430, letters_blue, NO_UPDATE, 1);
486 if(done == DONE_QUIT)
490 now_time = SDL_GetTicks();
491 if (now_time < last_time + FPS)
492 SDL_Delay(last_time + FPS - now_time); /* delay some time */
500 texture_free(&selection);
505 void le_change(float x, float y, unsigned char c)
510 level_change(¤t_level,x,y,c);
515 /* if there is a bad guy over there, remove it */
516 for(i = 0; i < NUM_BAD_GUYS; ++i)
517 if (bad_guys[i].alive)
518 if(xx == bad_guys[i].x/32 && yy == bad_guys[i].y/32)
519 bad_guys[i].alive = NO;
522 /* Save data for this level: */
530 /* Save data file: */
532 filename = (char *) malloc(sizeof(char) * (strlen(DATA_PREFIX) + 20) + strlen(level_subset));
533 sprintf(filename, "%s/levels/%s/level%d.dat", DATA_PREFIX, level_subset, level);
534 fi = fopen(filename, "w");
545 /* sptrinf("# Level created by SuperTux built-in editor", fi); */
547 fputs(current_level.name, fi);
549 fputs(current_level.theme, fi);
551 sprintf(str, "%d\n", current_level.time_left); /* time */
553 fputs(current_level.song_title, fi); /* song filename */
554 sprintf(str, "\n%d\n", current_level.bkgd_red); /* red background color */
556 sprintf(str, "%d\n", current_level.bkgd_green); /* green background color */
558 sprintf(str, "%d\n", current_level.bkgd_blue); /* blue background color */
560 sprintf(str, "%d\n", current_level.width); /* level width */
563 for(y = 0; y < 15; ++y)
565 fputs(current_level.tiles[y], fi);
571 drawcenteredtext("SAVED!", 240, letters_gold, NO_UPDATE, 1);
600 "./Del - Remove tile",
604 drawcenteredtext("- Help -", 30, letters_red, NO_UPDATE, 2);
605 drawtext("Keys:", 80, 60, letters_gold, NO_UPDATE, 1);
608 for(i = 0; i < sizeof(text)/sizeof(char *); i++)
609 drawtext(text[i], 40, 90+(i*16), letters_blue, NO_UPDATE, 1);
611 drawcenteredtext("Press Any Key to Continue", 460, letters_gold, NO_UPDATE, 1);
618 while(SDL_PollEvent(&event))
621 case SDL_MOUSEBUTTONDOWN: // mouse pressed
622 case SDL_KEYDOWN: // key pressed