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