4 // Copyright (C) 2004 SuperTux Development Team, see AUTHORS for details
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.
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.
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
30 #include "screen/screen.h"
35 #include "lispreader.h"
36 #include "resources.h"
37 #include "music_manager.h"
40 #include "background.h"
41 #include "lispwriter.h"
45 LevelSubset::LevelSubset()
50 LevelSubset::~LevelSubset()
55 void LevelSubset::create(const std::string& subset_name)
58 LevelSubset new_subset;
59 new_subset.name = subset_name;
60 new_subset.title = "Unknown Title";
61 new_subset.description = "No description so far.";
63 new_lev.init_defaults();
64 new_lev.save(subset_name, 1, 0);
67 void LevelSubset::parse (lisp_object_t* cursor)
69 while(!lisp_nil_p(cursor))
71 lisp_object_t* cur = lisp_car(cursor);
74 if (!lisp_cons_p(cur) || !lisp_symbol_p (lisp_car(cur)))
80 if (strcmp(lisp_symbol(lisp_car(cur)), "title") == 0)
82 if(( s = lisp_string(lisp_car(lisp_cdr(cur)))) != NULL)
87 else if (strcmp(lisp_symbol(lisp_car(cur)), "description") == 0)
89 if(( s = lisp_string(lisp_car(lisp_cdr(cur)))) != NULL)
95 cursor = lisp_cdr (cursor);
99 void LevelSubset::load(char *subset)
105 lisp_object_t* root_obj = 0;
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))
114 fi = fopen(filename, "r");
119 lisp_stream_t stream;
120 lisp_stream_init_file (&stream, fi);
121 root_obj = lisp_read (&stream);
123 if (root_obj->type == LISP_TYPE_EOF || root_obj->type == LISP_TYPE_PARSE_ERROR)
125 printf("World: Parse Error in file %s", filename);
128 lisp_object_t* cur = lisp_car(root_obj);
130 if (!lisp_symbol_p (cur))
132 printf("World: Read error in %s",filename);
135 if (strcmp(lisp_symbol(cur), "supertux-level-subset") == 0)
137 parse(lisp_cdr(root_obj));
144 snprintf(str, 1024, "%s.png", filename);
148 image = new Surface(str,IGNORE_ALPHA);
152 snprintf(filename, 1024, "%s/images/status/level-subset-info.png", datadir.c_str());
154 image = new Surface(filename,IGNORE_ALPHA);
158 for(i=1; i != -1; ++i)
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))
164 snprintf(filename, 1024, "%s/levels/%s/level%d.stl", datadir.c_str(), subset,i);
165 if(!faccessible(filename))
172 void LevelSubset::save()
177 /* Save data file: */
178 filename = "/levels/" + name + "/";
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()))
186 fi = fopen(filename.c_str(), "w");
189 perror(filename.c_str());
193 fprintf(fi,";SuperTux-Level-Subset\n");
194 fprintf(fi,"(supertux-level-subset\n");
196 /* Save title info: */
197 fprintf(fi," (title \"%s\")\n", title.c_str());
199 /* Save the description: */
200 fprintf(fi," (description \"%s\")\n", description.c_str());
220 Level::init_defaults()
224 song_title = "Mortimers_chipdisko.mod";
236 Level::load(const std::string& subset, int level, World* world)
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);
245 return load(filename, world);
249 Level::load(const std::string& filename, World* world)
251 lisp_object_t* root_obj = lisp_read_from_file(filename);
254 std::cout << "Level: Couldn't load file: " << filename << std::endl;
258 if (root_obj->type == LISP_TYPE_EOF || root_obj->type == LISP_TYPE_PARSE_ERROR)
261 std::cout << "World: Parse Error in file '" << filename
267 if (strcmp(lisp_symbol(lisp_car(root_obj)), "supertux-level") == 0)
269 LispReader reader(lisp_cdr(root_obj));
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;
277 if(!reader.read_int("time", &time_left)) {
278 printf("Warning: no time specified for level.\n");
282 if(!reader.read_int("height", &height)) {
283 printf("Warning: no height specified for level.\n");
287 // read old background stuff
289 reader.read_int("bkgd_speed", &bkgd_speed);
291 Color bkgd_top, bkgd_bottom;
293 reader.read_int("bkgd_red_top", &r);
294 reader.read_int("bkgd_green_top", &g);
295 reader.read_int("bkgd_blue_top", &b);
300 reader.read_int("bkgd_red_bottom", &r);
301 reader.read_int("bkgd_green_bottom", &g);
302 reader.read_int("bkgd_blue_bottom", &b);
304 bkgd_bottom.green = g;
305 bkgd_bottom.blue = b;
307 std::string bkgd_image;
308 reader.read_string("background", &bkgd_image);
311 Background* background = new Background();
313 background->set_image(bkgd_image, bkgd_speed);
315 background->set_gradient(bkgd_top, bkgd_bottom);
317 world->add_object(background);
321 reader.read_float("gravity", &gravity);
323 reader.read_string("name", &name);
324 author = "unknown author";
325 reader.read_string("author", &author);
327 reader.read_string("music", &song_title);
328 particle_system = "";
329 reader.read_string("particle_system", &particle_system);
331 reader.read_int_vector("background-tm", &bg_tiles);
332 if(int(bg_tiles.size()) != width * height)
333 st_abort("Wrong size of backgroundtilemap", "");
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", "");
340 reader.read_int_vector("foreground-tm", &fg_tiles);
341 if(int(fg_tiles.size()) != width * height)
342 st_abort("Wrong size of foregroundtilemap", "");
344 { // Read ResetPoints
345 lisp_object_t* cur = 0;
346 if (reader.read_lisp("reset-points", &cur))
348 while (!lisp_nil_p(cur))
350 lisp_object_t* data = lisp_car(cur);
354 LispReader reader(lisp_cdr(data));
355 if (reader.read_int("x", &pos.x)
356 && reader.read_int("y", &pos.y))
358 reset_points.push_back(pos);
367 lisp_object_t* cur = 0;
368 if (reader.read_lisp("objects", &cur))
371 world->parse_objects(cur);
376 lisp_object_t* cur = 0;
377 if (reader.read_lisp("camera", &cur))
379 LispReader reader(cur);
381 world->camera->read(reader);
391 /* Save data for level: */
394 Level::save(const std::string& subset, int level, World* world)
399 /* Save data file: */
400 snprintf(str, sizeof(str), "/levels/%s/", subset.c_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);
408 std::ofstream out(filename);
410 st_abort("Couldn't write file.", filename);
412 LispWriter writer(out);
415 writer.write_comment("SuperTux level made using the built-in leveleditor");
416 writer.start_list("supertux-level");
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);
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);
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");
441 writer.end_list("reset-points");
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);
450 serializable->write(writer);
452 writer.end_list("objects");
454 writer.end_list("supertux-level");
458 /* Unload data for this level: */
466 reset_points.clear();
472 /* Load a level-specific graphic... */
473 void Level::load_image(Surface** ptexture, string theme,const char * file, int use_alpha)
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);
481 *ptexture = new Surface(fname, use_alpha);
484 /* Change the size of a level */
486 Level::resize(int new_width, int new_height)
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];
499 ia_tiles.resize(new_width * new_height);
500 bg_tiles.resize(new_width * new_height);
501 fg_tiles.resize(new_width * new_height);
503 if(new_width > width) {
505 for(int y = std::min(height, new_height)-1; y >= 0; --y) {
506 for(int x = new_width-1; x >= 0; --x) {
508 ia_tiles[y * new_width + x] = 0;
509 bg_tiles[y * new_width + x] = 0;
510 fg_tiles[y * new_width + x] = 0;
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];
525 Level::change(float x, float y, int tm, unsigned int c)
527 int yy = ((int)y / 32);
528 int xx = ((int)x / 32);
530 if (yy >= 0 && yy < height && xx >= 0 && xx <= width)
535 bg_tiles[yy * width + xx] = c;
538 ia_tiles[yy * width + xx] = c;
541 fg_tiles[yy * width + xx] = c;
553 level_song = music_manager->load_music(datadir + "/music/" + song_title);
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;
564 level_song_fast = music_manager->load_music(song_path);
571 Level::get_level_music()
577 Level::get_level_music_fast()
579 return level_song_fast;
583 Level::gettileid(float x, float y) const
591 if (yy >= 0 && yy < height && xx >= 0 && xx <= width)
592 c = ia_tiles[yy * width + xx];
600 Level::get_tile_at(int x, int y) const
602 if(x < 0 || x >= width || y < 0 || y >= height)
605 return ia_tiles[y * width + x];