ab90191c525501b1ce1e170be26330d90b9bad6b
[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 <fstream>
27 #include "globals.h"
28 #include "setup.h"
29 #include "camera.h"
30 #include "screen/screen.h"
31 #include "level.h"
32 #include "physic.h"
33 #include "scene.h"
34 #include "tile.h"
35 #include "lispreader.h"
36 #include "resources.h"
37 #include "music_manager.h"
38 #include "gameobjs.h"
39 #include "world.h"
40 #include "background.h"
41 #include "lispwriter.h"
42
43 using namespace std;
44
45 LevelSubset::LevelSubset()
46     : image(0), levels(0)
47 {
48 }
49
50 LevelSubset::~LevelSubset()
51 {
52   delete image;
53 }
54
55 void LevelSubset::create(const std::string& subset_name)
56 {
57   Level new_lev;
58   LevelSubset new_subset;
59   new_subset.name = subset_name;
60   new_subset.title = "Unknown Title";
61   new_subset.description = "No description so far.";
62   new_subset.save();
63   new_lev.init_defaults();
64   new_lev.save(subset_name, 1, 0);
65 }
66
67 void LevelSubset::parse (lisp_object_t* cursor)
68 {
69   while(!lisp_nil_p(cursor))
70     {
71       lisp_object_t* cur = lisp_car(cursor);
72       char *s;
73
74       if (!lisp_cons_p(cur) || !lisp_symbol_p (lisp_car(cur)))
75         {
76           printf("Not good");
77         }
78       else
79         {
80           if (strcmp(lisp_symbol(lisp_car(cur)), "title") == 0)
81             {
82               if(( s = lisp_string(lisp_car(lisp_cdr(cur)))) != NULL)
83                 {
84                   title = s;
85                 }
86             }
87           else if (strcmp(lisp_symbol(lisp_car(cur)), "description") == 0)
88             {
89               if(( s = lisp_string(lisp_car(lisp_cdr(cur)))) != NULL)
90                 {
91                   description = s;
92                 }
93             }
94         }
95       cursor = lisp_cdr (cursor);
96     }
97 }
98
99 void LevelSubset::load(char *subset)
100 {
101   FILE* fi;
102   char filename[1024];
103   char str[1024];
104   int i;
105   lisp_object_t* root_obj = 0;
106
107   name = subset;
108
109   snprintf(filename, 1024, "%s/levels/%s/info", st_dir, subset);
110   if(!faccessible(filename))
111     snprintf(filename, 1024, "%s/levels/%s/info", datadir.c_str(), subset);
112   if(faccessible(filename))
113     {
114       fi = fopen(filename, "r");
115       if (fi == NULL)
116         {
117           perror(filename);
118         }
119       lisp_stream_t stream;
120       lisp_stream_init_file (&stream, fi);
121       root_obj = lisp_read (&stream);
122
123       if (root_obj->type == LISP_TYPE_EOF || root_obj->type == LISP_TYPE_PARSE_ERROR)
124         {
125           printf("World: Parse Error in file %s", filename);
126         }
127
128       lisp_object_t* cur = lisp_car(root_obj);
129
130       if (!lisp_symbol_p (cur))
131         {
132           printf("World: Read error in %s",filename);
133         }
134
135       if (strcmp(lisp_symbol(cur), "supertux-level-subset") == 0)
136         {
137           parse(lisp_cdr(root_obj));
138
139         }
140
141       lisp_free(root_obj);
142       fclose(fi);
143
144       snprintf(str, 1024, "%s.png", filename);
145       if(faccessible(str))
146         {
147           delete image;
148           image = new Surface(str,IGNORE_ALPHA);
149         }
150       else
151         {
152           snprintf(filename, 1024, "%s/images/status/level-subset-info.png", datadir.c_str());
153           delete image;
154           image = new Surface(filename,IGNORE_ALPHA);
155         }
156     }
157
158   for(i=1; i != -1; ++i)
159     {
160       /* Get the number of levels in this subset */
161       snprintf(filename, 1024, "%s/levels/%s/level%d.stl", st_dir, subset,i);
162       if(!faccessible(filename))
163         {
164           snprintf(filename, 1024, "%s/levels/%s/level%d.stl", datadir.c_str(), subset,i);
165           if(!faccessible(filename))
166             break;
167         }
168     }
169   levels = --i;
170 }
171
172 void LevelSubset::save()
173 {
174   FILE* fi;
175   string filename;
176
177   /* Save data file: */
178   filename = "/levels/" + name + "/";
179
180   fcreatedir(filename.c_str());
181   filename = string(st_dir) + "/levels/" + name + "/info";
182   if(!fwriteable(filename.c_str()))
183     filename = datadir + "/levels/" + name + "/info";
184   if(fwriteable(filename.c_str()))
185     {
186       fi = fopen(filename.c_str(), "w");
187       if (fi == NULL)
188         {
189           perror(filename.c_str());
190         }
191
192       /* Write header: */
193       fprintf(fi,";SuperTux-Level-Subset\n");
194       fprintf(fi,"(supertux-level-subset\n");
195
196       /* Save title info: */
197       fprintf(fi,"  (title \"%s\")\n", title.c_str());
198
199       /* Save the description: */
200       fprintf(fi,"  (description \"%s\")\n", description.c_str());
201
202       fprintf( fi,")");
203       fclose(fi);
204
205     }
206 }
207
208 Level::Level()
209   : img_bkgd(0)
210 {
211   init_defaults();
212 }
213
214 Level::~Level()
215 {
216   delete img_bkgd;
217 }
218
219 void
220 Level::init_defaults()
221 {
222   name       = "UnNamed";
223   author     = "UnNamed";
224   song_title = "Mortimers_chipdisko.mod";
225   width      = 0;
226   height     = 0;
227   start_pos.x = 100;
228   start_pos.y = 170;
229   time_left  = 100;
230   gravity    = 10.;
231
232   resize(21, 19);
233 }
234
235 int
236 Level::load(const std::string& subset, int level, World* world)
237 {
238   char filename[1024];
239
240   // Load data file:
241   snprintf(filename, 1024, "%s/levels/%s/level%d.stl", st_dir, subset.c_str(), level);
242   if(!faccessible(filename))
243     snprintf(filename, 1024, "%s/levels/%s/level%d.stl", datadir.c_str(), subset.c_str(), level);
244
245   return load(filename, world);
246 }
247
248 int 
249 Level::load(const std::string& filename, World* world)
250 {
251   lisp_object_t* root_obj = lisp_read_from_file(filename);
252   if (!root_obj)
253     {
254       std::cout << "Level: Couldn't load file: " << filename << std::endl;
255       return -1;
256     }
257
258   if (root_obj->type == LISP_TYPE_EOF || root_obj->type == LISP_TYPE_PARSE_ERROR)
259     {
260       lisp_free(root_obj);
261       std::cout << "World: Parse Error in file '" << filename
262                 << "'.\n";
263       return -1;
264     }
265
266   int version = 0;
267   if (strcmp(lisp_symbol(lisp_car(root_obj)), "supertux-level") == 0)
268     {
269       LispReader reader(lisp_cdr(root_obj));
270       version = 0;
271       reader.read_int("version",  &version);
272       if(!reader.read_int("width",  &width))
273         st_abort("No width specified for level.", "");
274       if (!reader.read_float("start_pos_x", &start_pos.x)) start_pos.x = 100;
275       if (!reader.read_float("start_pos_y", &start_pos.y)) start_pos.y = 170;
276       time_left = 500;
277       if(!reader.read_int("time",  &time_left)) {
278         printf("Warning: no time specified for level.\n");
279       }
280       
281       height = 15;
282       if(!reader.read_int("height",  &height)) {
283         printf("Warning: no height specified for level.\n");
284       }
285
286
287       // read old background stuff
288       int bkgd_speed = 50;
289       reader.read_int("bkgd_speed",  &bkgd_speed);
290       
291       Color bkgd_top, bkgd_bottom;
292       int r, g, b;
293       reader.read_int("bkgd_red_top", &r);
294       reader.read_int("bkgd_green_top",  &g);
295       reader.read_int("bkgd_blue_top",  &b);
296       bkgd_top.red = r;
297       bkgd_top.green = g;
298       bkgd_top.blue = b;
299
300       reader.read_int("bkgd_red_bottom",  &r);
301       reader.read_int("bkgd_green_bottom",  &g);
302       reader.read_int("bkgd_blue_bottom",  &b);
303       bkgd_bottom.red = r;
304       bkgd_bottom.green = g;
305       bkgd_bottom.blue = b;
306
307       std::string bkgd_image;
308       reader.read_string("background",  &bkgd_image);
309
310       if(world) {
311         Background* background = new Background();
312         if(bkgd_image != "")
313           background->set_image(bkgd_image, bkgd_speed);
314         else
315           background->set_gradient(bkgd_top, bkgd_bottom);
316
317         world->add_object(background);
318       }
319
320       gravity = 10;
321       reader.read_float("gravity",  &gravity);
322       name = "Noname";
323       reader.read_string("name",  &name);
324       author = "unknown author";
325       reader.read_string("author", &author);
326       song_title = "";
327       reader.read_string("music",  &song_title);
328       particle_system = "";
329       reader.read_string("particle_system", &particle_system);
330
331       reader.read_int_vector("background-tm",  &bg_tiles);
332       if(int(bg_tiles.size()) != width * height)
333         st_abort("Wrong size of backgroundtilemap", "");
334
335       if (!reader.read_int_vector("interactive-tm", &ia_tiles))
336         reader.read_int_vector("tilemap", &ia_tiles);
337       if(int(ia_tiles.size()) != width * height)
338         st_abort("Wrong size of interactivetilemap", "");      
339
340       reader.read_int_vector("foreground-tm",  &fg_tiles);
341       if(int(fg_tiles.size()) != width * height)
342         st_abort("Wrong size of foregroundtilemap", "");      
343
344       { // Read ResetPoints
345         lisp_object_t* cur = 0;
346         if (reader.read_lisp("reset-points",  &cur))
347           {
348             while (!lisp_nil_p(cur))
349               {
350                 lisp_object_t* data = lisp_car(cur);
351
352                 ResetPoint pos;
353
354                 LispReader reader(lisp_cdr(data));
355                 if (reader.read_int("x", &pos.x)
356                     && reader.read_int("y", &pos.y))
357                   {
358                     reset_points.push_back(pos);
359                   }
360
361                 cur = lisp_cdr(cur);
362               }
363           }
364       }
365
366       { // Read Objects
367         lisp_object_t* cur = 0;
368         if (reader.read_lisp("objects",  &cur))
369           {
370             if(world)
371               world->parse_objects(cur);
372           }
373       }
374
375       { // Read Camera
376         lisp_object_t* cur = 0;
377         if (reader.read_lisp("camera", &cur))
378           {
379             LispReader reader(cur);
380             if(world) {
381               world->camera->read(reader);
382             }
383           }
384       }
385     }
386
387   lisp_free(root_obj);
388   return 0;
389 }
390
391 /* Save data for level: */
392
393 void 
394 Level::save(const std::string& subset, int level, World* world)
395 {
396   char filename[1024];
397   char str[80];
398
399   /* Save data file: */
400   snprintf(str, sizeof(str), "/levels/%s/", subset.c_str());
401   fcreatedir(str);
402   snprintf(filename, sizeof(filename),
403       "%s/levels/%s/level%d.stl", st_dir, subset.c_str(), level);
404   if(!fwriteable(filename))
405     snprintf(filename, sizeof(filename), "%s/levels/%s/level%d.stl",
406         datadir.c_str(), subset.c_str(), level);
407
408   std::ofstream out(filename);
409   if(!out.good()) {
410     st_abort("Couldn't write file.", filename);
411   }
412   LispWriter writer(out);
413
414   /* Write header: */
415   writer.write_comment("SuperTux level made using the built-in leveleditor");
416   writer.start_list("supertux-level");
417
418   writer.write_int("version", 1);
419   writer.write_string("name", name);
420   writer.write_string("author", author);
421   writer.write_string("music", song_title);
422   writer.write_string("background", bkgd_image);
423   writer.write_string("particle_system", particle_system);
424   writer.write_int("time", time_left);
425   writer.write_int("width", width);
426   writer.write_int("height", height);
427   writer.write_float("gravity", gravity);
428
429   writer.write_int_vector("background-tm", bg_tiles);
430   writer.write_int_vector("interactive-tm", ia_tiles);
431   writer.write_int_vector("foreground-tm", fg_tiles);
432
433   writer.start_list("reset-points");
434   for(std::vector<ResetPoint>::iterator i = reset_points.begin();
435       i != reset_points.end(); ++i) {
436     writer.start_list("point");
437     writer.write_int("x", i->x);
438     writer.write_int("y", i->y);
439     writer.end_list("point");
440   }
441   writer.end_list("reset-points");
442
443   // write objects
444   writer.start_list("objects");
445   // pick all objects that can be written into a levelfile
446   for(std::vector<GameObject*>::iterator it = world->gameobjects.begin();
447       it != world->gameobjects.end(); ++it) {
448     Serializable* serializable = dynamic_cast<Serializable*> (*it);
449     if(serializable)
450       serializable->write(writer);
451   }
452   writer.end_list("objects");
453
454   writer.end_list("supertux-level");
455   out.close();
456 }
457
458 /* Unload data for this level: */
459 void
460 Level::cleanup()
461 {
462   bg_tiles.clear();
463   ia_tiles.clear();
464   fg_tiles.clear();
465
466   reset_points.clear();
467   name = "";
468   author = "";
469   song_title = "";
470 }
471
472 /* Load a level-specific graphic... */
473 void Level::load_image(Surface** ptexture, string theme,const  char * file, int use_alpha)
474 {
475   char fname[1024];
476
477   snprintf(fname, 1024, "%s/themes/%s/%s", st_dir, theme.c_str(), file);
478   if(!faccessible(fname))
479     snprintf(fname, 1024, "%s/images/themes/%s/%s", datadir.c_str(), theme.c_str(), file);
480
481   *ptexture = new Surface(fname, use_alpha);
482 }
483
484 /* Change the size of a level */
485 void 
486 Level::resize(int new_width, int new_height)
487 {
488   if(new_width < width) {
489     // remap tiles for new width
490     for(int y = 0; y < height && y < new_height; ++y) {
491       for(int x = 0; x < new_width; ++x) {
492         ia_tiles[y * new_width + x] = ia_tiles[y * width + x];
493         bg_tiles[y * new_width + x] = bg_tiles[y * width + x];
494         fg_tiles[y * new_width + x] = fg_tiles[y * width + x];
495       }
496     }
497   }
498
499   ia_tiles.resize(new_width * new_height);
500   bg_tiles.resize(new_width * new_height);
501   fg_tiles.resize(new_width * new_height); 
502
503   if(new_width > width) {
504     // remap tiles
505     for(int y = std::min(height, new_height)-1; y >= 0; --y) {
506       for(int x = new_width-1; x >= 0; --x) {
507         if(x >= width) {
508           ia_tiles[y * new_width + x] = 0;
509           bg_tiles[y * new_width + x] = 0;
510           fg_tiles[y * new_width + x] = 0;
511         } else {
512           ia_tiles[y * new_width + x] = ia_tiles[y * width + x];
513           bg_tiles[y * new_width + x] = bg_tiles[y * width + x];
514           fg_tiles[y * new_width + x] = fg_tiles[y * width + x];
515         }
516       }
517     }
518   }
519
520   height = new_height;
521   width = new_width;
522 }
523
524 void
525 Level::change(float x, float y, int tm, unsigned int c)
526 {
527   int yy = ((int)y / 32);
528   int xx = ((int)x / 32);
529
530   if (yy >= 0 && yy < height && xx >= 0 && xx <= width)
531     {
532       switch(tm)
533         {
534         case TM_BG:
535           bg_tiles[yy * width + xx] = c;
536           break;
537         case TM_IA:
538           ia_tiles[yy * width + xx] = c;
539           break;
540         case TM_FG:
541           fg_tiles[yy * width + xx] = c;
542           break;
543         }
544     }
545 }
546
547 void
548 Level::load_song()
549 {
550   char* song_path;
551   char* song_subtitle;
552
553   level_song = music_manager->load_music(datadir + "/music/" + song_title);
554
555   song_path = (char *) malloc(sizeof(char) * datadir.length() +
556                               strlen(song_title.c_str()) + 8 + 5);
557   song_subtitle = strdup(song_title.c_str());
558   strcpy(strstr(song_subtitle, "."), "\0");
559   sprintf(song_path, "%s/music/%s-fast%s", datadir.c_str(), 
560           song_subtitle, strstr(song_title.c_str(), "."));
561   if(!music_manager->exists_music(song_path)) {
562     level_song_fast = level_song;
563   } else {
564     level_song_fast = music_manager->load_music(song_path);
565   }
566   free(song_subtitle);
567   free(song_path);
568 }
569
570 MusicRef
571 Level::get_level_music()
572 {
573   return level_song;
574 }
575
576 MusicRef
577 Level::get_level_music_fast()
578 {
579   return level_song_fast;
580 }
581
582 unsigned int 
583 Level::gettileid(float x, float y) const
584 {
585   int xx, yy;
586   unsigned int c;
587
588   yy = ((int)y / 32);
589   xx = ((int)x / 32);
590
591   if (yy >= 0 && yy < height && xx >= 0 && xx <= width)
592     c = ia_tiles[yy * width + xx];
593   else
594     c = 0;
595
596   return c;
597 }
598
599 unsigned int
600 Level::get_tile_at(int x, int y) const
601 {
602   if(x < 0 || x >= width || y < 0 || y >= height)
603     return 0;
604   
605   return ia_tiles[y * width + x];
606 }
607
608 /* EOF */