- moved stuff from gamesession to world
[supertux.git] / src / level.cpp
1 //
2 // C Implementation: level
3 //
4 // Description:
5 //
6 //
7 // Author: Tobias Glaesser <tobi.web@gmx.de>, (C) 2003
8 //
9 // Copyright: See COPYING file that comes with this distribution
10 //
11 //
12
13 #include <map>
14 #include <stdlib.h>
15 #include <stdio.h>
16 #include <string.h>
17 #include <iostream>
18 #include "globals.h"
19 #include "setup.h"
20 #include "screen.h"
21 #include "level.h"
22 #include "physic.h"
23 #include "scene.h"
24 #include "tile.h"
25 #include "lispreader.h"
26
27 using namespace std;
28
29 texture_type img_bkgd, img_bkgd_tile[2][4], img_solid[4], img_brick[2];
30
31 st_subset::st_subset()
32 {
33   levels = 0;
34 }
35
36 void st_subset::create(const std::string& subset_name)
37 {
38   Level new_lev;
39   st_subset new_subset;
40   new_subset.name = subset_name;
41   new_subset.title = "Unknown Title";
42   new_subset.description = "No description so far.";
43   new_subset.save();
44   new_lev.init_defaults();
45   new_lev.save(subset_name.c_str(),1);
46 }
47
48 void st_subset::parse (lisp_object_t* cursor)
49 {
50   while(!lisp_nil_p(cursor))
51     {
52       lisp_object_t* cur = lisp_car(cursor);
53       char *s;
54
55       if (!lisp_cons_p(cur) || !lisp_symbol_p (lisp_car(cur)))
56         {
57           printf("Not good");
58         }
59       else
60         {
61           if (strcmp(lisp_symbol(lisp_car(cur)), "title") == 0)
62             {
63               if(( s = lisp_string(lisp_car(lisp_cdr(cur)))) != NULL)
64                 {
65                   title = s;
66                 }
67             }
68           else if (strcmp(lisp_symbol(lisp_car(cur)), "description") == 0)
69             {
70               if(( s = lisp_string(lisp_car(lisp_cdr(cur)))) != NULL)
71                 {
72                   description = s;
73                 }
74             }
75         }
76       cursor = lisp_cdr (cursor);
77     }
78 }
79
80 void st_subset::load(char *subset)
81 {
82   FILE* fi;
83   char filename[1024];
84   char str[1024];
85   int i;
86   lisp_object_t* root_obj = 0;
87
88   name = subset;
89
90   snprintf(filename, 1024, "%s/levels/%s/info", st_dir, subset);
91   if(!faccessible(filename))
92     snprintf(filename, 1024, "%s/levels/%s/info", datadir.c_str(), subset);
93   if(faccessible(filename))
94     {
95       fi = fopen(filename, "r");
96       if (fi == NULL)
97         {
98           perror(filename);
99         }
100       lisp_stream_t stream;
101       lisp_stream_init_file (&stream, fi);
102       root_obj = lisp_read (&stream);
103
104       if (root_obj->type == LISP_TYPE_EOF || root_obj->type == LISP_TYPE_PARSE_ERROR)
105         {
106           printf("World: Parse Error in file %s", filename);
107         }
108
109       lisp_object_t* cur = lisp_car(root_obj);
110
111       if (!lisp_symbol_p (cur))
112         {
113           printf("World: Read error in %s",filename);
114         }
115
116       if (strcmp(lisp_symbol(cur), "supertux-level-subset") == 0)
117         {
118           parse(lisp_cdr(root_obj));
119
120         }
121
122       fclose(fi);
123
124       snprintf(str, 1024, "%s.png", filename);
125       if(faccessible(str))
126         {
127           texture_load(&image,str,IGNORE_ALPHA);
128         }
129       else
130         {
131           snprintf(filename, 1024, "%s/images/status/level-subset-info.png", datadir.c_str());
132           texture_load(&image,filename,IGNORE_ALPHA);
133         }
134     }
135
136   for(i=1; i != -1; ++i)
137     {
138       /* Get the number of levels in this subset */
139       snprintf(filename, 1024, "%s/levels/%s/level%d.stl", st_dir, subset,i);
140       if(!faccessible(filename))
141         {
142           snprintf(filename, 1024, "%s/levels/%s/level%d.stl", datadir.c_str(), subset,i);
143           if(!faccessible(filename))
144             break;
145         }
146     }
147   levels = --i;
148 }
149
150 void st_subset::save()
151 {
152   FILE* fi;
153   string filename;
154
155   /* Save data file: */
156   filename = "/levels/" + name + "/";
157
158   fcreatedir(filename.c_str());
159   filename = string(st_dir) + "/levels/" + name + "/info";
160   if(!fwriteable(filename.c_str()))
161     filename = datadir + "/levels/" + name + "/info";
162   if(fwriteable(filename.c_str()))
163     {
164       fi = fopen(filename.c_str(), "w");
165       if (fi == NULL)
166         {
167           perror(filename.c_str());
168         }
169
170       /* Write header: */
171       fprintf(fi,";SuperTux-Level-Subset\n");
172       fprintf(fi,"(supertux-level-subset\n");
173
174       /* Save title info: */
175       fprintf(fi,"  (title \"%s\")\n", title.c_str());
176
177       /* Save the description: */
178       fprintf(fi,"  (description \"%s\")\n", description.c_str());
179
180       fprintf( fi,")");
181       fclose(fi);
182
183     }
184 }
185
186 void st_subset::free()
187 {
188   title.clear();
189   description.clear();
190   name.clear();
191   texture_free(&image);
192   levels = 0;
193 }
194
195 void
196 Level::init_defaults()
197 {
198   name       = "UnNamed";
199   theme      = "antarctica";
200   song_title = "Mortimers_chipdisko.mod";
201   bkgd_image = "arctis.png";
202   width      = 21;
203   time_left  = 100;
204   gravity    = 10.;
205   bkgd_red   = 0;
206   bkgd_green = 0;
207   bkgd_blue  = 0;
208
209   for(int i = 0; i < 15; ++i)
210     {
211       ia_tiles[i] = (unsigned int*) malloc((width+1)*sizeof(unsigned int));
212       ia_tiles[i][width] = (unsigned int) '\0';
213       for(int y = 0; y < width; ++y)
214         ia_tiles[i][y] = 0;
215       ia_tiles[i][width] = (unsigned int) '\0';
216
217       bg_tiles[i] = (unsigned int*) malloc((width+1)*sizeof(unsigned int));
218       bg_tiles[i][width] = (unsigned int) '\0';
219       for(int y = 0; y < width; ++y)
220         bg_tiles[i][y] = 0;
221       bg_tiles[i][width] = (unsigned int) '\0';
222
223       fg_tiles[i] = (unsigned int*) malloc((width+1)*sizeof(unsigned int));
224       fg_tiles[i][width] = (unsigned int) '\0';
225       for(int y = 0; y < width; ++y)
226         fg_tiles[i][y] = 0;
227       fg_tiles[i][width] = (unsigned int) '\0';
228     }
229 }
230
231 int
232 Level::load(const  char *subset, int level)
233 {
234   char filename[1024];
235
236   // Load data file:
237   snprintf(filename, 1024, "%s/levels/%s/level%d.stl", st_dir, subset, level);
238   if(!faccessible(filename))
239     snprintf(filename, 1024, "%s/levels/%s/level%d.stl", datadir.c_str(), subset, level);
240
241   return load(filename);
242 }
243
244 int 
245 Level::load(const std::string& filename)
246 {
247   FILE * fi;
248   lisp_object_t* root_obj = 0;
249   fi = fopen(filename.c_str(), "r");
250   if (fi == NULL)
251     {
252       perror(filename.c_str());
253       return -1;
254     }
255
256   lisp_stream_t stream;
257   lisp_stream_init_file (&stream, fi);
258   root_obj = lisp_read (&stream);
259
260   if (root_obj->type == LISP_TYPE_EOF || root_obj->type == LISP_TYPE_PARSE_ERROR)
261     {
262       printf("World: Parse Error in file %s", filename.c_str());
263     }
264
265   vector<int> ia_tm;
266   vector<int> bg_tm;
267   vector<int> fg_tm;
268
269   int version = 0;
270   if (strcmp(lisp_symbol(lisp_car(root_obj)), "supertux-level") == 0)
271     {
272       LispReader reader(lisp_cdr(root_obj));
273
274       reader.read_int("version",  &version);
275       reader.read_int("width",  &width);
276       reader.read_int("time",  &time_left);
277       reader.read_int("bkgd_red",  &bkgd_red);
278       reader.read_int("bkgd_green",  &bkgd_green);
279       reader.read_int("bkgd_blue",  &bkgd_blue);
280       reader.read_float("gravity",  &gravity);
281       reader.read_string("name",  &name);
282       reader.read_string("theme",  &theme);
283       reader.read_string("music",  &song_title);
284       reader.read_string("background",  &bkgd_image);
285       reader.read_string("particle_system", &particle_system);
286       reader.read_int_vector("background-tm",  &bg_tm);
287
288       if (!reader.read_int_vector("interactive-tm", &ia_tm))
289         reader.read_int_vector("tilemap", &ia_tm);
290
291       reader.read_int_vector("foreground-tm",  &fg_tm);
292
293       {
294         lisp_object_t* cur = 0;
295         if (reader.read_lisp("objects",  &cur))
296           {
297             while (!lisp_nil_p(cur))
298               {
299                 lisp_object_t* data = lisp_car(cur);
300
301                 BadGuyData bg_data;
302                 bg_data.kind = badguykind_from_string(lisp_symbol(lisp_car(data)));
303                 LispReader reader(lisp_cdr(data));
304                 reader.read_int("x", &bg_data.x);
305                 reader.read_int("y", &bg_data.y);
306
307                 badguy_data.push_back(bg_data);
308
309                 cur = lisp_cdr(cur);
310               }
311           }
312       }
313
314       // Convert old levels to the new tile numbers
315       if (version == 0)
316         {
317           std::map<char, int> transtable;
318           transtable['.'] = 0;
319           transtable['x'] = 104;
320           transtable['X'] = 77;
321           transtable['y'] = 78;
322           transtable['Y'] = 105;
323           transtable['A'] = 83;
324           transtable['B'] = 102;
325           transtable['!'] = 103;
326           transtable['a'] = 84;
327           transtable['C'] = 85;
328           transtable['D'] = 86;
329           transtable['E'] = 87;
330           transtable['F'] = 88;
331           transtable['c'] = 89;
332           transtable['d'] = 90;
333           transtable['e'] = 91;
334           transtable['f'] = 92;
335
336           transtable['G'] = 93;
337           transtable['H'] = 94;
338           transtable['I'] = 95;
339           transtable['J'] = 96;
340
341           transtable['g'] = 97;
342           transtable['h'] = 98;
343           transtable['i'] = 99;
344           transtable['j'] = 100
345                             ;
346           transtable['#'] = 11;
347           transtable['['] = 13;
348           transtable['='] = 14;
349           transtable[']'] = 15;
350           transtable['$'] = 82;
351           transtable['^'] = 76;
352           transtable['*'] = 80;
353           transtable['|'] = 79;
354           transtable['\\'] = 81;
355           transtable['&'] = 75;
356
357           int x = 0;
358           int y = 0;
359           for(std::vector<int>::iterator i = ia_tm.begin(); i != ia_tm.end(); ++i)
360             {
361               if (*i == '0' || *i == '1' || *i == '2')
362                 {
363                   badguy_data.push_back(BadGuyData(static_cast<BadGuyKind>(*i-'0'),
364                                                 x*32, y*32));
365                   *i = 0;
366                 }
367               else
368                 {
369                   std::map<char, int>::iterator j = transtable.find(*i);
370                   if (j != transtable.end())
371                     *i = j->second;
372                   else
373                     printf("Error: conversion will fail, unsupported char: '%c' (%d)\n", *i, *i);
374                 }
375               ++x;
376               if (x >= width)
377                 {
378                   x = 0;
379                   ++y;
380                 }
381             }
382         }
383     }
384
385   for(int i = 0; i < 15; ++i)
386     {
387       ia_tiles[i] = (unsigned int*) calloc((width +1) , sizeof(unsigned int) );
388       bg_tiles[i] = (unsigned int*) calloc((width +1) , sizeof(unsigned int) );
389       fg_tiles[i] = (unsigned int*) calloc((width +1) , sizeof(unsigned int) );
390     }
391
392   int i = 0;
393   int j = 0;
394   for(vector<int>::iterator it = ia_tm.begin(); it != ia_tm.end(); ++it, ++i)
395     {
396       ia_tiles[j][i] = (*it);
397       if(i == width - 1)
398         {
399           i = -1;
400           ++j;
401         }
402     }
403
404   i = j = 0;
405   for(vector<int>::iterator it = bg_tm.begin(); it != bg_tm.end(); ++it, ++i)
406     {
407
408       bg_tiles[j][i] = (*it);
409       if(i == width - 1)
410         {
411           i = -1;
412           ++j;
413         }
414     }
415
416   i = j = 0;
417   for(vector<int>::iterator it = fg_tm.begin(); it != fg_tm.end(); ++it, ++i)
418     {
419
420       fg_tiles[j][i] = (*it);
421       if(i == width - 1)
422         {
423           i = -1;
424           ++j;
425         }
426     }
427
428   // FIXME: Set the global gravity to the latest loaded level's gravity
429   ::gravity = gravity;
430
431   //  Mark the end position of this level!
432   // FIXME: -10 is a rather random value, we still need some kind of
433   // real levelend gola
434   endpos = 32*(width-10);
435
436   fclose(fi);
437   return 0;
438 }
439
440 /* Save data for level: */
441
442 void 
443 Level::save(const  char * subset, int level)
444 {
445   char filename[1024];
446   char str[80];
447
448   /* Save data file: */
449   sprintf(str, "/levels/%s/", subset);
450   fcreatedir(str);
451   snprintf(filename, 1024, "%s/levels/%s/level%d.stl", st_dir, subset, level);
452   if(!fwriteable(filename))
453     snprintf(filename, 1024, "%s/levels/%s/level%d.stl", datadir.c_str(), subset, level);
454
455   FILE * fi = fopen(filename, "w");
456   if (fi == NULL)
457     {
458       perror(filename);
459       st_shutdown();
460       exit(-1);
461     }
462
463
464   /* Write header: */
465   fprintf(fi,";SuperTux-Level\n");
466   fprintf(fi,"(supertux-level\n");
467
468   fprintf(fi,"  (version %d)\n", 1);
469   fprintf(fi,"  (name \"%s\")\n", name.c_str());
470   fprintf(fi,"  (theme \"%s\")\n", theme.c_str());
471   fprintf(fi,"  (music \"%s\")\n", song_title.c_str());
472   fprintf(fi,"  (background \"%s\")\n", bkgd_image.c_str());
473   fprintf(fi,"  (particle_system \"%s\")\n", particle_system.c_str());
474   fprintf(fi,"  (bkgd_red %d)\n", bkgd_red);
475   fprintf(fi,"  (bkgd_green %d)\n", bkgd_green);
476   fprintf(fi,"  (bkgd_blue %d)\n", bkgd_blue);
477   fprintf(fi,"  (time %d)\n", time_left);
478   fprintf(fi,"  (width %d)\n", width);
479   fprintf(fi,"  (gravity %2.1f)\n", gravity);
480   fprintf(fi,"  (background-tm ");
481
482   for(int y = 0; y < 15; ++y)
483     {
484       for(int i = 0; i < width; ++i)
485         fprintf(fi," %d ", bg_tiles[y][i]);
486     }
487
488   fprintf( fi,")\n");
489   fprintf(fi,"  (interactive-tm ");
490
491   for(int y = 0; y < 15; ++y)
492     {
493       for(int i = 0; i < width; ++i)
494         fprintf(fi," %d ", ia_tiles[y][i]);
495     }
496
497   fprintf( fi,")\n");
498   fprintf(fi,"  (foreground-tm ");
499
500   for(int y = 0; y < 15; ++y)
501     {
502       for(int i = 0; i < width; ++i)
503         fprintf(fi," %d ", fg_tiles[y][i]);
504     }
505
506   fprintf( fi,")\n");
507   fprintf( fi,"(objects\n");
508
509   for(std::vector<BadGuyData>::iterator it = badguy_data.begin();
510       it != badguy_data.end();
511       ++it)
512     fprintf( fi,"(%s (x %d) (y %d))\n",badguykind_to_string((*it).kind).c_str(),(*it).x,(*it).y);
513
514   fprintf( fi,")\n");
515
516   fprintf( fi,")\n");
517
518   fclose(fi);
519 }
520
521
522 /* Unload data for this level: */
523
524 void
525 Level::cleanup()
526 {
527   for(int i=0; i < 15; ++i)
528     free(bg_tiles[i]);
529   for(int i=0; i < 15; ++i)
530     free(ia_tiles[i]);
531   for(int i=0; i < 15; ++i)
532     free(fg_tiles[i]);
533
534   name.clear();
535   theme.clear();
536   song_title.clear();
537   bkgd_image.clear();
538
539   badguy_data.clear();
540 }
541
542 /* Load graphics: */
543
544 void 
545 Level::load_gfx()
546 {
547   level_load_image(&img_brick[0],theme,"brick0.png", IGNORE_ALPHA);
548   level_load_image(&img_brick[1],theme,"brick1.png", IGNORE_ALPHA);
549
550   level_load_image(&img_solid[0],theme,"solid0.png", USE_ALPHA);
551   level_load_image(&img_solid[1],theme,"solid1.png", USE_ALPHA);
552   level_load_image(&img_solid[2],theme,"solid2.png", USE_ALPHA);
553   level_load_image(&img_solid[3],theme,"solid3.png", USE_ALPHA);
554
555   level_load_image(&img_bkgd_tile[0][0],theme,"bkgd-00.png", USE_ALPHA);
556   level_load_image(&img_bkgd_tile[0][1],theme,"bkgd-01.png", USE_ALPHA);
557   level_load_image(&img_bkgd_tile[0][2],theme,"bkgd-02.png", USE_ALPHA);
558   level_load_image(&img_bkgd_tile[0][3],theme,"bkgd-03.png", USE_ALPHA);
559
560   level_load_image(&img_bkgd_tile[1][0],theme,"bkgd-10.png", USE_ALPHA);
561   level_load_image(&img_bkgd_tile[1][1],theme,"bkgd-11.png", USE_ALPHA);
562   level_load_image(&img_bkgd_tile[1][2],theme,"bkgd-12.png", USE_ALPHA);
563   level_load_image(&img_bkgd_tile[1][3],theme,"bkgd-13.png", USE_ALPHA);
564
565   if(!bkgd_image.empty())
566     {
567       char fname[1024];
568       snprintf(fname, 1024, "%s/background/%s", st_dir, bkgd_image.c_str());
569       if(!faccessible(fname))
570         snprintf(fname, 1024, "%s/images/background/%s", datadir.c_str(), bkgd_image.c_str());
571       texture_load(&img_bkgd, fname, IGNORE_ALPHA);
572     }
573   else
574     {
575       /* Quick hack to make sure an image is loaded, when we are freeing it afterwards. */#
576       level_load_image(&img_bkgd, theme,"solid0.png", IGNORE_ALPHA);
577     }
578 }
579
580 /* Free graphics data for this level: */
581 void level_free_gfx(void)
582 {
583   int i;
584
585   for (i = 0; i < 2; i++)
586     {
587       texture_free(&img_brick[i]);
588     }
589   for (i = 0; i < 4; i++)
590     {
591       texture_free(&img_solid[i]);
592       texture_free(&img_bkgd_tile[0][i]);
593       texture_free(&img_bkgd_tile[1][i]);
594     }
595
596   texture_free(&img_bkgd);
597 }
598
599 /* Load a level-specific graphic... */
600
601 void level_load_image(texture_type* ptexture, string theme,const  char * file, int use_alpha)
602 {
603   char fname[1024];
604
605   snprintf(fname, 1024, "%s/themes/%s/%s", st_dir, theme.c_str(), file);
606   if(!faccessible(fname))
607     snprintf(fname, 1024, "%s/images/themes/%s/%s", datadir.c_str(), theme.c_str(), file);
608
609   texture_load(ptexture, fname, use_alpha);
610 }
611
612 void tilemap_change_size(unsigned int** tilemap[15], int w, int old_w)
613 {
614   int j,y;
615   for(y = 0; y < 15; ++y)
616     {
617       *tilemap[y] = (unsigned int*) realloc(*tilemap[y],(w+1)*sizeof(unsigned int));
618       if(w > old_w)
619         for(j = 0; j < w - old_w; ++j)
620           *tilemap[y][old_w+j] = 0;
621       *tilemap[y][w] = 0;
622     }
623 }
624
625 /* Change the size of a level (width) */
626 void 
627 Level::change_size (int new_width)
628 {
629   if(new_width < 21)
630     new_width = 21;
631
632   tilemap_change_size((unsigned int***)&ia_tiles, new_width, width);
633   tilemap_change_size((unsigned int***)&bg_tiles, new_width, width);
634   tilemap_change_size((unsigned int***)&fg_tiles, new_width, width);
635
636   width = new_width;
637 }
638
639 void
640 Level::change(float x, float y, int tm, unsigned int c)
641 {
642   int yy = ((int)y / 32);
643   int xx = ((int)x / 32);
644
645   if (yy >= 0 && yy < 15 && xx >= 0 && xx <= width)
646     {
647       switch(tm)
648         {
649         case TM_BG:
650           bg_tiles[yy][xx] = c;
651           break;
652         case TM_IA:
653           ia_tiles[yy][xx] = c;
654           break;
655         case TM_FG:
656           fg_tiles[yy][xx] = c;
657           break;
658         }
659     }
660 }
661
662 /* Free music data for this level: */
663
664 void level_free_song(void)
665 {
666   free_music(level_song);
667   free_music(level_song_fast);
668 }
669
670 /* Load music: */
671
672 void
673 Level::load_song()
674 {
675   char* song_path;
676   char* song_subtitle;
677
678   level_song = ::load_song(datadir + "/music/" + song_title);
679
680   song_path = (char *) malloc(sizeof(char) * datadir.length() +
681                               strlen(song_title.c_str()) + 8 + 5);
682   song_subtitle = strdup(song_title.c_str());
683   strcpy(strstr(song_subtitle, "."), "\0");
684   sprintf(song_path, "%s/music/%s-fast%s", datadir.c_str(), song_subtitle, strstr(song_title.c_str(), "."));
685   level_song_fast = ::load_song(song_path);
686   free(song_subtitle);
687   free(song_path);
688 }
689
690
691 unsigned int 
692 Level::gettileid(float x, float y)
693 {
694   int xx, yy;
695   unsigned int c;
696
697   yy = ((int)y / 32);
698   xx = ((int)x / 32);
699
700   if (yy >= 0 && yy < 15 && xx >= 0 && xx <= width)
701     c = ia_tiles[yy][xx];
702   else
703     c = 0;
704
705   return c;
706 }
707
708 /* EOF */