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
34 #include "lispreader.h"
35 #include "resources.h"
36 #include "music_manager.h"
39 #include "lispwriter.h"
43 LevelSubset::LevelSubset()
48 LevelSubset::~LevelSubset()
53 void LevelSubset::create(const std::string& subset_name)
56 LevelSubset new_subset;
57 new_subset.name = subset_name;
58 new_subset.title = "Unknown Title";
59 new_subset.description = "No description so far.";
61 new_lev.init_defaults();
62 new_lev.save(subset_name, 1, 0);
65 void LevelSubset::parse (lisp_object_t* cursor)
67 while(!lisp_nil_p(cursor))
69 lisp_object_t* cur = lisp_car(cursor);
72 if (!lisp_cons_p(cur) || !lisp_symbol_p (lisp_car(cur)))
78 if (strcmp(lisp_symbol(lisp_car(cur)), "title") == 0)
80 if(( s = lisp_string(lisp_car(lisp_cdr(cur)))) != NULL)
85 else if (strcmp(lisp_symbol(lisp_car(cur)), "description") == 0)
87 if(( s = lisp_string(lisp_car(lisp_cdr(cur)))) != NULL)
93 cursor = lisp_cdr (cursor);
97 void LevelSubset::load(char *subset)
103 lisp_object_t* root_obj = 0;
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))
112 fi = fopen(filename, "r");
117 lisp_stream_t stream;
118 lisp_stream_init_file (&stream, fi);
119 root_obj = lisp_read (&stream);
121 if (root_obj->type == LISP_TYPE_EOF || root_obj->type == LISP_TYPE_PARSE_ERROR)
123 printf("World: Parse Error in file %s", filename);
126 lisp_object_t* cur = lisp_car(root_obj);
128 if (!lisp_symbol_p (cur))
130 printf("World: Read error in %s",filename);
133 if (strcmp(lisp_symbol(cur), "supertux-level-subset") == 0)
135 parse(lisp_cdr(root_obj));
142 snprintf(str, 1024, "%s.png", filename);
146 image = new Surface(str,IGNORE_ALPHA);
150 snprintf(filename, 1024, "%s/images/status/level-subset-info.png", datadir.c_str());
152 image = new Surface(filename,IGNORE_ALPHA);
156 for(i=1; i != -1; ++i)
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))
162 snprintf(filename, 1024, "%s/levels/%s/level%d.stl", datadir.c_str(), subset,i);
163 if(!faccessible(filename))
170 void LevelSubset::save()
175 /* Save data file: */
176 filename = "/levels/" + name + "/";
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()))
184 fi = fopen(filename.c_str(), "w");
187 perror(filename.c_str());
191 fprintf(fi,";SuperTux-Level-Subset\n");
192 fprintf(fi,"(supertux-level-subset\n");
194 /* Save title info: */
195 fprintf(fi," (title \"%s\")\n", title.c_str());
197 /* Save the description: */
198 fprintf(fi," (description \"%s\")\n", description.c_str());
218 Level::init_defaults()
222 song_title = "Mortimers_chipdisko.mod";
223 bkgd_image = "arctis.jpg";
234 bkgd_bottom.red = 255;
235 bkgd_bottom.green = 255;
236 bkgd_bottom.blue = 255;
242 Level::load(const std::string& subset, int level, World* world)
247 snprintf(filename, 1024, "%s/levels/%s/level%d.stl", st_dir, subset.c_str(), level);
248 if(!faccessible(filename))
249 snprintf(filename, 1024, "%s/levels/%s/level%d.stl", datadir.c_str(), subset.c_str(), level);
251 return load(filename, world);
255 Level::load(const std::string& filename, World* world)
257 lisp_object_t* root_obj = lisp_read_from_file(filename);
260 std::cout << "Level: Couldn't load file: " << filename << std::endl;
264 if (root_obj->type == LISP_TYPE_EOF || root_obj->type == LISP_TYPE_PARSE_ERROR)
267 std::cout << "World: Parse Error in file '" << filename
273 if (strcmp(lisp_symbol(lisp_car(root_obj)), "supertux-level") == 0)
275 LispReader reader(lisp_cdr(root_obj));
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_float("start_pos_x", &start_pos.x)) start_pos.x = 100;
281 if (!reader.read_float("start_pos_y", &start_pos.y)) start_pos.y = 170;
283 if(!reader.read_int("time", &time_left)) {
284 printf("Warning: no time specified for level.\n");
288 if(!reader.read_int("height", &height)) {
289 printf("Warning: no height specified for level.\n");
293 reader.read_int("bkgd_speed", &bkgd_speed);
295 bkgd_top.red = bkgd_top.green = bkgd_top.blue = 0;
296 reader.read_int("bkgd_red_top", &bkgd_top.red);
297 reader.read_int("bkgd_green_top", &bkgd_top.green);
298 reader.read_int("bkgd_blue_top", &bkgd_top.blue);
300 bkgd_bottom.red = bkgd_bottom.green = bkgd_bottom.blue = 0;
301 reader.read_int("bkgd_red_bottom", &bkgd_bottom.red);
302 reader.read_int("bkgd_green_bottom", &bkgd_bottom.green);
303 reader.read_int("bkgd_blue_bottom", &bkgd_bottom.blue);
306 reader.read_float("gravity", &gravity);
308 reader.read_string("name", &name);
309 author = "unknown author";
310 reader.read_string("author", &author);
312 reader.read_string("music", &song_title);
314 reader.read_string("background", &bkgd_image);
315 particle_system = "";
316 reader.read_string("particle_system", &particle_system);
318 reader.read_int_vector("background-tm", &bg_tiles);
319 if(int(bg_tiles.size()) != width * height)
320 st_abort("Wrong size of backgroundtilemap", "");
322 if (!reader.read_int_vector("interactive-tm", &ia_tiles))
323 reader.read_int_vector("tilemap", &ia_tiles);
324 if(int(ia_tiles.size()) != width * height)
325 st_abort("Wrong size of interactivetilemap", "");
327 reader.read_int_vector("foreground-tm", &fg_tiles);
328 if(int(fg_tiles.size()) != width * height)
329 st_abort("Wrong size of foregroundtilemap", "");
331 { // Read ResetPoints
332 lisp_object_t* cur = 0;
333 if (reader.read_lisp("reset-points", &cur))
335 while (!lisp_nil_p(cur))
337 lisp_object_t* data = lisp_car(cur);
341 LispReader reader(lisp_cdr(data));
342 if (reader.read_int("x", &pos.x)
343 && reader.read_int("y", &pos.y))
345 reset_points.push_back(pos);
354 lisp_object_t* cur = 0;
355 if (reader.read_lisp("objects", &cur))
358 world->parse_objects(cur);
363 lisp_object_t* cur = 0;
364 if (reader.read_lisp("camera", &cur))
366 LispReader reader(cur);
368 world->camera->read(reader);
378 /* Save data for level: */
381 Level::save(const std::string& subset, int level, World* world)
386 /* Save data file: */
387 snprintf(str, sizeof(str), "/levels/%s/", subset.c_str());
389 snprintf(filename, sizeof(filename),
390 "%s/levels/%s/level%d.stl", st_dir, subset.c_str(), level);
391 if(!fwriteable(filename))
392 snprintf(filename, sizeof(filename), "%s/levels/%s/level%d.stl",
393 datadir.c_str(), subset.c_str(), level);
395 std::ofstream out(filename);
397 st_abort("Couldn't write file.", filename);
399 LispWriter writer(out);
402 writer.write_comment("SuperTux level made using the built-in leveleditor");
403 writer.start_list("supertux-level");
405 writer.write_int("version", 1);
406 writer.write_string("name", name);
407 writer.write_string("author", author);
408 writer.write_string("music", song_title);
409 writer.write_string("background", bkgd_image);
410 writer.write_string("particle_system", particle_system);
411 writer.write_int("bkgd_speed", bkgd_speed);
412 writer.write_int("bkgd_red_top", bkgd_top.red);
413 writer.write_int("bkgd_green_top", bkgd_top.green);
414 writer.write_int("bkgd_blue_top", bkgd_top.blue);
415 writer.write_int("bkgd_red_bottom", bkgd_bottom.red);
416 writer.write_int("bkgd_green_bottom", bkgd_bottom.green);
417 writer.write_int("bkgd_blue_bottom", bkgd_bottom.blue);
418 writer.write_int("time", time_left);
419 writer.write_int("width", width);
420 writer.write_int("height", height);
421 writer.write_float("gravity", gravity);
423 writer.write_int_vector("background-tm", bg_tiles);
424 writer.write_int_vector("interactive-tm", ia_tiles);
425 writer.write_int_vector("foreground-tm", fg_tiles);
427 writer.start_list("reset-points");
428 for(std::vector<ResetPoint>::iterator i = reset_points.begin();
429 i != reset_points.end(); ++i) {
430 writer.start_list("point");
431 writer.write_int("x", i->x);
432 writer.write_int("y", i->y);
433 writer.end_list("point");
435 writer.end_list("reset-points");
438 writer.start_list("objects");
439 // pick all objects that can be written into a levelfile
440 for(std::vector<GameObject*>::iterator it = world->gameobjects.begin();
441 it != world->gameobjects.end(); ++it) {
442 Serializable* serializable = dynamic_cast<Serializable*> (*it);
444 serializable->write(writer);
446 writer.end_list("objects");
448 writer.end_list("supertux-level");
452 /* Unload data for this level: */
460 reset_points.clear();
470 if(!bkgd_image.empty())
473 snprintf(fname, 1024, "%s/background/%s", st_dir, bkgd_image.c_str());
474 if(!faccessible(fname))
475 snprintf(fname, 1024, "%s/images/background/%s", datadir.c_str(), bkgd_image.c_str());
477 img_bkgd = new Surface(fname, IGNORE_ALPHA);
486 /* Load a level-specific graphic... */
487 void Level::load_image(Surface** ptexture, string theme,const char * file, int use_alpha)
491 snprintf(fname, 1024, "%s/themes/%s/%s", st_dir, theme.c_str(), file);
492 if(!faccessible(fname))
493 snprintf(fname, 1024, "%s/images/themes/%s/%s", datadir.c_str(), theme.c_str(), file);
495 *ptexture = new Surface(fname, use_alpha);
498 /* Change the size of a level */
500 Level::resize(int new_width, int new_height)
502 if(new_width < width) {
503 // remap tiles for new width
504 for(int y = 0; y < height && y < new_height; ++y) {
505 for(int x = 0; x < new_width; ++x) {
506 ia_tiles[y * new_width + x] = ia_tiles[y * width + x];
507 bg_tiles[y * new_width + x] = bg_tiles[y * width + x];
508 fg_tiles[y * new_width + x] = fg_tiles[y * width + x];
513 ia_tiles.resize(new_width * new_height);
514 bg_tiles.resize(new_width * new_height);
515 fg_tiles.resize(new_width * new_height);
517 if(new_width > width) {
519 for(int y = std::min(height, new_height)-1; y >= 0; --y) {
520 for(int x = new_width-1; x >= 0; --x) {
522 ia_tiles[y * new_width + x] = 0;
523 bg_tiles[y * new_width + x] = 0;
524 fg_tiles[y * new_width + x] = 0;
526 ia_tiles[y * new_width + x] = ia_tiles[y * width + x];
527 bg_tiles[y * new_width + x] = bg_tiles[y * width + x];
528 fg_tiles[y * new_width + x] = fg_tiles[y * width + x];
539 Level::change(float x, float y, int tm, unsigned int c)
541 int yy = ((int)y / 32);
542 int xx = ((int)x / 32);
544 if (yy >= 0 && yy < height && xx >= 0 && xx <= width)
549 bg_tiles[yy * width + xx] = c;
552 ia_tiles[yy * width + xx] = c;
555 fg_tiles[yy * width + xx] = c;
567 level_song = music_manager->load_music(datadir + "/music/" + song_title);
569 song_path = (char *) malloc(sizeof(char) * datadir.length() +
570 strlen(song_title.c_str()) + 8 + 5);
571 song_subtitle = strdup(song_title.c_str());
572 strcpy(strstr(song_subtitle, "."), "\0");
573 sprintf(song_path, "%s/music/%s-fast%s", datadir.c_str(),
574 song_subtitle, strstr(song_title.c_str(), "."));
575 if(!music_manager->exists_music(song_path)) {
576 level_song_fast = level_song;
578 level_song_fast = music_manager->load_music(song_path);
585 Level::get_level_music()
591 Level::get_level_music_fast()
593 return level_song_fast;
597 Level::gettileid(float x, float y) const
605 if (yy >= 0 && yy < height && xx >= 0 && xx <= width)
606 c = ia_tiles[yy * width + xx];
614 Level::get_tile_at(int x, int y) const
616 if(x < 0 || x >= width || y < 0 || y >= height)
619 return ia_tiles[y * width + x];