21069c7fd72f956bd4a14f854036fce6d7f57ca4
[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 <cstdlib>
23 #include <cstdio>
24 #include <cstring>
25 #include <iostream>
26 #include <fstream>
27 #include <stdexcept>
28
29 #include "app/globals.h"
30 #include "app/setup.h"
31 #include "camera.h"
32 #include "video/screen.h"
33 #include "level.h"
34 #include "math/physic.h"
35 #include "scene.h"
36 #include "sector.h"
37 #include "tile.h"
38 #include "utils/lispreader.h"
39 #include "resources.h"
40 #include "gameobjs.h"
41 #include "utils/lispwriter.h"
42 #include "tilemap.h"
43
44 using namespace std;
45
46 Level::Level()
47   : name("noname"), author("mr. x"), time_left(500)
48
49 {
50 }
51
52 void
53 Level::load(const std::string& filename)
54 {
55   std::string filepath;
56   filepath = st_dir + "/levels/" + filename;
57   if (access(filepath.c_str(), R_OK) != 0)
58   {
59     filepath = datadir + "/levels/" + filename;
60     if (access(filepath.c_str(), R_OK) != 0)
61     {
62       std::cerr << "Error: Level: couldn't find level: " << filename << std::endl;
63       return;
64     }
65   }
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 == "name") {
84       name = lisp_string(data);
85     } else if(token == "author") {
86       author = lisp_string(data);
87     } else if(token == "time") {
88       time_left = lisp_integer(data);
89     } else if(token == "sector") {
90       Sector* sector = new Sector;
91       sector->parse(reader);
92       add_sector(sector);
93     } else {
94       std::cerr << "Unknown token '" << token << "' in level file.\n";
95       continue;
96     }
97   }
98   
99   delete level;
100 }
101
102 void
103 Level::load_old_format(LispReader& reader)
104 {
105   reader.read_string("name", name, true);
106   reader.read_string("author", author);
107   reader.read_int("time", time_left);
108
109   Sector* sector = new Sector;
110   sector->parse_old_format(reader);
111   add_sector(sector);
112 }
113
114 void
115 Level::save(const std::string& filename)
116 {
117  std::string filepath = "levels/" + filename;
118  int last_slash = filepath.find_last_of('/');
119  FileSystem::fcreatedir(filepath.substr(0,last_slash).c_str());
120  filepath = st_dir + "/" + filepath;
121  ofstream file(filepath.c_str(), ios::out);
122  LispWriter* writer = new LispWriter(file);
123
124  writer->write_comment("Level made using SuperTux's built-in Level Editor");
125
126  writer->start_list("supertux-level");
127
128  int version = 2;
129  writer->write_int("version", version);
130
131  writer->write_string("name", name);
132  writer->write_string("author", author);
133  writer->write_int("time", time_left);
134
135  for(Sectors::iterator i = sectors.begin(); i != sectors.end(); ++i)
136    {
137    writer->start_list("sector");
138    i->second->write(*writer);
139    writer->end_list("sector");
140    }
141
142  writer->end_list("supertux-level");
143
144  delete writer;
145  file.close();
146 }
147
148 Level::~Level()
149 {
150   for(Sectors::iterator i = sectors.begin(); i != sectors.end(); ++i)
151     delete i->second;
152 }
153
154 void
155 Level::do_vertical_flip()
156 {
157   for(Sectors::iterator i = sectors.begin(); i != sectors.end(); ++i)
158     i->second->do_vertical_flip();
159 }
160
161 void
162 Level::add_sector(Sector* sector)
163 {
164   sectors.insert(std::make_pair(sector->get_name(), sector));       
165 }
166
167 Sector*
168 Level::get_sector(const std::string& name)
169 {
170   Sectors::iterator i = sectors.find(name);
171   if(i == sectors.end())
172     return 0;
173
174   return i->second;
175 }
176
177 Sector*
178 Level::get_next_sector(const Sector* sector)
179 {
180   for(Sectors::iterator i = sectors.begin(); i != sectors.end(); ++i)
181     {
182     if(i->second == sector)
183       {
184       i++;
185       if(i == sectors.end())
186         return NULL;
187       return i->second;
188       }
189     }
190   std::cerr << "Warning: Sector not found on level\n";
191   return NULL;
192 }
193
194 Sector*
195 Level::get_previous_sector(const Sector* sector)
196 {
197   for(Sectors::iterator i = sectors.begin(); i != sectors.end(); ++i)
198     {
199     if(i->second == sector)
200       {
201       if(i == sectors.begin())
202         return NULL;
203       i--;
204       return i->second;
205       }
206     }
207   std::cerr << "Warning: Sector not found on level\n";
208   return NULL;
209 }
210
211 int
212 Level::get_total_sectors()
213 {
214 return sectors.size();
215 }
216
217 int
218 Level::get_total_badguys()
219 {
220   int total_badguys = 0;
221   for(Sectors::iterator i = sectors.begin(); i != sectors.end(); ++i)
222     total_badguys += i->second->get_total_badguys();
223   return total_badguys;
224 }
225
226 int
227 Level::get_total_coins()
228 {
229   int total_coins = 0;
230   for(Sectors::iterator it = sectors.begin(); it != sectors.end(); ++it)
231     for(int x = 0; static_cast<unsigned int>(x) < it->second->solids->get_width(); x++)
232       for(int y = 0; static_cast<unsigned int>(y) < it->second->solids->get_height(); y++)
233         if(it->second->solids->get_tile(x,y)->attributes & Tile::COIN)
234           total_coins++;
235   return total_coins;
236 }