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