make supertux accepts normal paths on the commandline
[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 <config.h>
22
23 #include <map>
24 #include <cstdlib>
25 #include <cstdio>
26 #include <cstring>
27 #include <iostream>
28 #include <fstream>
29 #include <stdexcept>
30
31 #include "app/globals.h"
32 #include "app/setup.h"
33 #include "camera.h"
34 #include "video/screen.h"
35 #include "level.h"
36 #include "math/physic.h"
37 #include "scene.h"
38 #include "sector.h"
39 #include "tile.h"
40 #include "utils/lispreader.h"
41 #include "resources.h"
42 #include "gameobjs.h"
43 #include "utils/lispwriter.h"
44 #include "tilemap.h"
45
46 using namespace std;
47
48 Level::Level()
49   : name("noname"), author("mr. x"), timelimit(500),
50     end_sequence_type(NONE_ENDSEQ_ANIM) 
51 {
52 }
53
54 void
55 Level::create(const std::string& filename)
56 {
57   Level level;
58   const size_t width = 25;
59   const size_t height = 19;
60   level.add_sector(Sector::create("main", width, height));
61   level.save(filename);
62 }
63
64 void
65 Level::load(const std::string& filepath)
66 {
67   LispReader* level = LispReader::load(filepath, "supertux-level");
68
69   int version = 1;
70   level->read_int("version", version);
71   if(version == 1) {
72     load_old_format(*level);
73     delete level;
74     return;
75   }
76
77   for(lisp_object_t* cur = level->get_lisp(); !lisp_nil_p(cur);
78       cur = lisp_cdr(cur)) {
79     std::string token = lisp_symbol(lisp_car(lisp_car(cur)));
80     lisp_object_t* data = lisp_car(lisp_cdr(lisp_car(cur)));
81     LispReader reader(lisp_cdr(lisp_car(cur)));
82
83     if(token == "version") {
84       if(lisp_integer(data) > 2) {
85         std::cerr << "Warning: level format newer than application.\n";
86       }
87     } else if(token == "name") {
88       name = lisp_string(data);
89     } else if(token == "author") {
90       author = lisp_string(data);
91     } else if(token == "time") {
92       timelimit = lisp_integer(data);
93     } else if(token == "sector") {
94       Sector* sector = new Sector;
95       sector->parse(reader);
96       add_sector(sector);
97     } else if(token == "end-sequence-animation") {
98       std::string endsequencename = lisp_string(data);
99       if(endsequencename == "fireworks") {
100         end_sequence_type = FIREWORKS_ENDSEQ_ANIM;
101       } else {
102         std::cout << "Unknown endsequence type: '" << endsequencename <<
103           "'.\n";
104       }
105     } else {
106       std::cerr << "Unknown token '" << token << "' in level file.\n";
107       continue;
108     }
109   }
110   
111   delete level;
112 }
113
114 void
115 Level::load_old_format(LispReader& reader)
116 {
117   reader.read_string("name", name, true);
118   reader.read_string("author", author);
119   reader.read_int("time", timelimit);
120
121   Sector* sector = new Sector;
122   sector->parse_old_format(reader);
123   add_sector(sector);
124 }
125
126 void
127 Level::save(const std::string& filename)
128 {
129  std::string filepath = "levels/" + filename;
130  int last_slash = filepath.find_last_of('/');
131  FileSystem::fcreatedir(filepath.substr(0,last_slash).c_str());
132  filepath = st_dir + "/" + filepath;
133  ofstream file(filepath.c_str(), ios::out);
134  LispWriter* writer = new LispWriter(file);
135
136  writer->write_comment("Level made using SuperTux's built-in Level Editor");
137
138  writer->start_list("supertux-level");
139
140  int version = 2;
141  writer->write_int("version", version);
142
143  writer->write_string("name", name);
144  writer->write_string("author", author);
145  writer->write_int("time", timelimit);
146  writer->write_string("end-sequence-animation",
147      end_sequence_type == FIREWORKS_ENDSEQ_ANIM ? "fireworks" : "none");
148
149  for(Sectors::iterator i = sectors.begin(); i != sectors.end(); ++i) {
150    writer->start_list("sector");
151    i->second->write(*writer);
152    writer->end_list("sector");
153  }
154
155  writer->end_list("supertux-level");
156
157  delete writer;
158  file.close();
159 }
160
161 Level::~Level()
162 {
163   for(Sectors::iterator i = sectors.begin(); i != sectors.end(); ++i)
164     delete i->second;
165 }
166
167 void
168 Level::do_vertical_flip()
169 {
170   for(Sectors::iterator i = sectors.begin(); i != sectors.end(); ++i)
171     i->second->do_vertical_flip();
172 }
173
174 void
175 Level::add_sector(Sector* sector)
176 {
177   sectors.insert(std::make_pair(sector->get_name(), sector));       
178 }
179
180 Sector*
181 Level::get_sector(const std::string& name)
182 {
183   Sectors::iterator i = sectors.find(name);
184   if(i == sectors.end())
185     return 0;
186
187   return i->second;
188 }
189
190 Sector*
191 Level::get_next_sector(const Sector* sector)
192 {
193   for(Sectors::iterator i = sectors.begin(); i != sectors.end(); ++i)
194     {
195     if(i->second == sector)
196       {
197       i++;
198       if(i == sectors.end())
199         return NULL;
200       return i->second;
201       }
202     }
203   std::cerr << "Warning: Sector not found on level\n";
204   return NULL;
205 }
206
207 Sector*
208 Level::get_previous_sector(const Sector* sector)
209 {
210   for(Sectors::iterator i = sectors.begin(); i != sectors.end(); ++i)
211     {
212     if(i->second == sector)
213       {
214       if(i == sectors.begin())
215         return NULL;
216       i--;
217       return i->second;
218       }
219     }
220   std::cerr << "Warning: Sector not found on level\n";
221   return NULL;
222 }
223
224 int
225 Level::get_total_sectors()
226 {
227 return sectors.size();
228 }
229
230 int
231 Level::get_total_badguys()
232 {
233   int total_badguys = 0;
234   for(Sectors::iterator i = sectors.begin(); i != sectors.end(); ++i)
235     total_badguys += i->second->get_total_badguys();
236   return total_badguys;
237 }
238
239 int
240 Level::get_total_coins()
241 {
242   int total_coins = 0;
243   for(Sectors::iterator i = sectors.begin(); i != sectors.end(); ++i) {
244     TileMap* solids = i->second->solids;
245     if(!solids) {
246       std::cerr << "Sector '" << i->first << "' contains no solids!?!\n";
247       continue;
248     }
249     for(size_t x = 0; x < solids->get_width(); ++x)
250       for(size_t y = 0; y < solids->get_height(); ++y) {
251         const Tile* tile = solids->get_tile(x, y);
252         if(tile == 0) {
253           std::cerr << "Invalid tile in sector '" << i->first << "'.\n";
254           continue;
255         }
256         if(tile->attributes & Tile::COIN)
257           total_coins++;
258       }
259   }
260   return total_coins;
261 }