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";
230 back_scrolling = false;
231 hor_autoscroll_speed = 0;
236 bkgd_bottom.red = 255;
237 bkgd_bottom.green = 255;
238 bkgd_bottom.blue = 255;
244 Level::load(const std::string& subset, int level, World* world)
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);
253 return load(filename, world);
257 Level::load(const std::string& filename, World* world)
259 lisp_object_t* root_obj = lisp_read_from_file(filename);
262 std::cout << "Level: Couldn't load file: " << filename << std::endl;
266 if (root_obj->type == LISP_TYPE_EOF || root_obj->type == LISP_TYPE_PARSE_ERROR)
268 printf("World: Parse Error in file %s", filename.c_str());
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_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;
283 if(!reader.read_int("time", &time_left)) {
284 printf("Warning no time specified for level.\n");
288 reader.read_int("height", &height);
290 back_scrolling = false;
291 reader.read_bool("back_scrolling", &back_scrolling);
293 hor_autoscroll_speed = 0;
294 reader.read_float("hor_autoscroll_speed", &hor_autoscroll_speed);
297 reader.read_int("bkgd_speed", &bkgd_speed);
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);
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);
311 reader.read_float("gravity", &gravity);
313 reader.read_string("name", &name);
314 author = "unknown author";
315 reader.read_string("author", &author);
317 reader.read_string("music", &song_title);
319 reader.read_string("background", &bkgd_image);
320 particle_system = "";
321 reader.read_string("particle_system", &particle_system);
323 reader.read_int_vector("background-tm", &bg_tiles);
324 if(int(bg_tiles.size()) != width * height)
325 st_abort("Wrong size of backgroundtilemap", "");
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", "");
332 reader.read_int_vector("foreground-tm", &fg_tiles);
333 if(int(fg_tiles.size()) != width * height)
334 st_abort("Wrong size of foregroundtilemap", "");
336 { // Read ResetPoints
337 lisp_object_t* cur = 0;
338 if (reader.read_lisp("reset-points", &cur))
340 while (!lisp_nil_p(cur))
342 lisp_object_t* data = lisp_car(cur);
346 LispReader reader(lisp_cdr(data));
347 if (reader.read_int("x", &pos.x)
348 && reader.read_int("y", &pos.y))
350 reset_points.push_back(pos);
359 lisp_object_t* cur = 0;
360 if (reader.read_lisp("objects", &cur))
363 world->parse_objects(cur);
367 #if 0 // TODO fix this or remove it
368 // Convert old levels to the new tile numbers
371 std::map<char, int> transtable;
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;
390 transtable['G'] = 93;
391 transtable['H'] = 94;
392 transtable['I'] = 95;
393 transtable['J'] = 96;
395 transtable['g'] = 97;
396 transtable['h'] = 98;
397 transtable['i'] = 99;
398 transtable['j'] = 100
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;
413 for(std::vector<int>::iterator i = ia_tm.begin(); i != ia_tm.end(); ++i)
415 if (*i == '0' || *i == '1' || *i == '2')
417 badguy_data.push_back(BadGuyData(static_cast<BadGuyKind>(*i-'0'),
423 std::map<char, int>::iterator j = transtable.find(*i);
424 if (j != transtable.end())
427 printf("Error: conversion will fail, unsupported char: '%c' (%d)\n", *i, *i);
444 /* Save data for level: */
447 Level::save(const std::string& subset, int level, World* world)
452 /* Save data file: */
453 sprintf(str, "/levels/%s/", subset.c_str());
455 snprintf(filename, 1024, "%s/levels/%s/level%d.stl", st_dir, subset.c_str(),
457 if(!fwriteable(filename))
458 snprintf(filename, 1024, "%s/levels/%s/level%d.stl", datadir.c_str(),
459 subset.c_str(), level);
461 std::ofstream out(filename);
463 st_abort("Couldn't write file.", filename);
465 LispWriter writer(out);
468 writer.write_comment("SuperTux level made using the built-in leveleditor");
469 writer.start_list("supertux-level");
471 writer.write_int("version", 1);
472 writer.write_string("name", name);
473 writer.write_string("author", author);
474 writer.write_string("music", song_title);
475 writer.write_string("background", bkgd_image);
476 writer.write_string("particle_system", particle_system);
477 writer.write_int("bkgd_speed", bkgd_speed);
478 writer.write_int("bkgd_red_top", bkgd_top.red);
479 writer.write_int("bkgd_green_top", bkgd_top.green);
480 writer.write_int("bkgd_blue_top", bkgd_top.blue);
481 writer.write_int("bkgd_red_bottom", bkgd_bottom.red);
482 writer.write_int("bkgd_green_bottom", bkgd_bottom.green);
483 writer.write_int("bkgd_blue_bottom", bkgd_bottom.blue);
484 writer.write_int("time", time_left);
485 writer.write_int("width", width);
486 writer.write_int("height", height);
487 writer.write_bool("back_scrolling", back_scrolling);
488 writer.write_float("hor_autoscroll_speed", hor_autoscroll_speed);
489 writer.write_float("gravity", gravity);
491 writer.write_int_vector("background-tm", bg_tiles);
492 writer.write_int_vector("interactive-tm", ia_tiles);
493 writer.write_int_vector("foreground-tm", fg_tiles);
495 writer.start_list("reset-points");
496 for(std::vector<ResetPoint>::iterator i = reset_points.begin();
497 i != reset_points.end(); ++i) {
498 writer.start_list("point");
499 writer.write_int("x", i->x);
500 writer.write_int("y", i->y);
501 writer.end_list("point");
503 writer.end_list("reset-points");
506 writer.start_list("objects");
507 // pick all objects that can be written into a levelfile
508 for(std::vector<GameObject*>::iterator it = world->gameobjects.begin();
509 it != world->gameobjects.end(); ++it) {
510 Serializable* serializable = dynamic_cast<Serializable*> (*it);
512 serializable->write(writer);
514 writer.end_list("objects");
516 writer.end_list("supertux-level");
520 /* Unload data for this level: */
528 reset_points.clear();
538 if(!bkgd_image.empty())
541 snprintf(fname, 1024, "%s/background/%s", st_dir, bkgd_image.c_str());
542 if(!faccessible(fname))
543 snprintf(fname, 1024, "%s/images/background/%s", datadir.c_str(), bkgd_image.c_str());
545 img_bkgd = new Surface(fname, IGNORE_ALPHA);
554 /* Load a level-specific graphic... */
555 void Level::load_image(Surface** ptexture, string theme,const char * file, int use_alpha)
559 snprintf(fname, 1024, "%s/themes/%s/%s", st_dir, theme.c_str(), file);
560 if(!faccessible(fname))
561 snprintf(fname, 1024, "%s/images/themes/%s/%s", datadir.c_str(), theme.c_str(), file);
563 *ptexture = new Surface(fname, use_alpha);
566 /* Change the size of a level */
568 Level::resize(int new_width, int new_height)
570 if(new_width < width) {
571 // remap tiles for new width
572 for(int y = 0; y < height && y < new_height; ++y) {
573 for(int x = 0; x < new_width; ++x) {
574 ia_tiles[y * new_width + x] = ia_tiles[y * width + x];
575 bg_tiles[y * new_width + x] = bg_tiles[y * width + x];
576 fg_tiles[y * new_width + x] = fg_tiles[y * width + x];
581 ia_tiles.resize(new_width * new_height);
582 bg_tiles.resize(new_width * new_height);
583 fg_tiles.resize(new_width * new_height);
585 if(new_width > width) {
587 for(int y = std::min(height, new_height)-1; y >= 0; --y) {
588 for(int x = new_width-1; x >= 0; --x) {
590 ia_tiles[y * new_width + x] = 0;
591 bg_tiles[y * new_width + x] = 0;
592 fg_tiles[y * new_width + x] = 0;
594 ia_tiles[y * new_width + x] = ia_tiles[y * width + x];
595 bg_tiles[y * new_width + x] = bg_tiles[y * width + x];
596 fg_tiles[y * new_width + x] = fg_tiles[y * width + x];
607 Level::change(float x, float y, int tm, unsigned int c)
609 int yy = ((int)y / 32);
610 int xx = ((int)x / 32);
612 if (yy >= 0 && yy < height && xx >= 0 && xx <= width)
617 bg_tiles[yy * width + xx] = c;
620 ia_tiles[yy * width + xx] = c;
623 fg_tiles[yy * width + xx] = c;
635 level_song = music_manager->load_music(datadir + "/music/" + song_title);
637 song_path = (char *) malloc(sizeof(char) * datadir.length() +
638 strlen(song_title.c_str()) + 8 + 5);
639 song_subtitle = strdup(song_title.c_str());
640 strcpy(strstr(song_subtitle, "."), "\0");
641 sprintf(song_path, "%s/music/%s-fast%s", datadir.c_str(),
642 song_subtitle, strstr(song_title.c_str(), "."));
643 if(!music_manager->exists_music(song_path)) {
644 level_song_fast = level_song;
646 level_song_fast = music_manager->load_music(song_path);
653 Level::get_level_music()
659 Level::get_level_music_fast()
661 return level_song_fast;
665 Level::gettileid(float x, float y) const
673 if (yy >= 0 && yy < height && xx >= 0 && xx <= width)
674 c = ia_tiles[yy * width + xx];
682 Level::get_tile_at(int x, int y) const
684 if(x < 0 || x >= width || y < 0 || y >= height)
687 return ia_tiles[y * width + x];