3a080672b19d03ae12b9ee1310d638cfc4cea1ce
[supertux.git] / src / worldmap.cpp
1 //  $Id$
2 //
3 //  SuperTux -  A Jump'n Run
4 //  Copyright (C) 2004 Ingo Ruhnke <grumbel@gmx.de>
5 //  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
6 //
7 //  This program is free software; you can redistribute it and/or
8 //  modify it under the terms of the GNU General Public License
9 //  as published by the Free Software Foundation; either version 2
10 //  of the License, or (at your option) any later version.
11 //
12 //  This program is distributed in the hope that it will be useful,
13 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
14 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 //  GNU General Public License for more details.
16 //
17 //  You should have received a copy of the GNU General Public License
18 //  along with this program; if not, write to the Free Software
19 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20 #include <config.h>
21
22 #include <iostream>
23 #include <fstream>
24 #include <vector>
25 #include <cassert>
26 #include <stdexcept>
27 #include <sstream>
28 #include <unistd.h>
29 #include <physfs.h>
30
31 #include "gettext.hpp"
32 #include "msg.hpp"
33 #include "mainloop.hpp"
34 #include "video/surface.hpp"
35 #include "video/screen.hpp"
36 #include "video/drawing_context.hpp"
37 #include "sprite/sprite_manager.hpp"
38 #include "audio/sound_manager.hpp"
39 #include "lisp/parser.hpp"
40 #include "lisp/lisp.hpp"
41 #include "lisp/list_iterator.hpp"
42 #include "lisp/writer.hpp"
43 #include "game_session.hpp"
44 #include "sector.hpp"
45 #include "worldmap.hpp"
46 #include "resources.hpp"
47 #include "misc.hpp"
48 #include "msg.hpp"
49 #include "player_status.hpp"
50 #include "textscroller.hpp"
51 #include "main.hpp"
52 #include "spawn_point.hpp"
53 #include "file_system.hpp"
54 #include "gui/menu.hpp"
55 #include "gui/mousecursor.hpp"
56 #include "control/joystickkeyboardcontroller.hpp"
57 #include "object/background.hpp"
58 #include "object/tilemap.hpp"
59
60 Menu* worldmap_menu  = 0;
61
62 static const float TUXSPEED = 200;
63 static const float map_message_TIME = 2.8;
64
65 namespace WorldMapNS {
66
67 WorldMap* WorldMap::current_ = NULL;
68
69 Direction reverse_dir(Direction direction)
70 {
71   switch(direction)
72     {
73     case D_WEST:
74       return D_EAST;
75     case D_EAST:
76       return D_WEST;
77     case D_NORTH:
78       return D_SOUTH;
79     case D_SOUTH:
80       return D_NORTH;
81     case D_NONE:
82       return D_NONE;
83     }
84   return D_NONE;
85 }
86
87 std::string
88 direction_to_string(Direction direction)
89 {
90   switch(direction)
91     {
92     case D_WEST:
93       return "west";
94     case D_EAST:
95       return "east";
96     case D_NORTH:
97       return "north";
98     case D_SOUTH:
99       return "south";
100     default:
101       return "none";
102     }
103 }
104
105 Direction
106 string_to_direction(const std::string& directory)
107 {
108   if (directory == "west")
109     return D_WEST;
110   else if (directory == "east")
111     return D_EAST;
112   else if (directory == "north")
113     return D_NORTH;
114   else if (directory == "south")
115     return D_SOUTH;
116   else
117     return D_NONE;
118 }
119
120 //---------------------------------------------------------------------------
121
122 Tux::Tux(WorldMap* worldmap_)
123   : worldmap(worldmap_)
124 {
125   tux_sprite = sprite_manager->create("images/worldmap/common/tux.sprite");
126   
127   offset = 0;
128   moving = false;
129   direction = D_NONE;
130   input_direction = D_NONE;
131 }
132
133 Tux::~Tux()
134 {
135   delete tux_sprite;
136 }
137
138 void
139 Tux::draw(DrawingContext& context)
140 {
141   switch (player_status->bonus) {
142     case GROWUP_BONUS:
143       tux_sprite->set_action(moving ? "large-walking" : "large-stop");
144       break;
145     case FIRE_BONUS:
146       tux_sprite->set_action(moving ? "fire-walking" : "fire-stop");
147       break;
148     case NO_BONUS:
149       tux_sprite->set_action(moving ? "small-walking" : "small-stop");
150       break;
151     default:
152       msg_debug << "Bonus type not handled in worldmap." << std::endl;
153       tux_sprite->set_action("large-stop");
154       break;
155   }
156
157   tux_sprite->draw(context, get_pos(), LAYER_OBJECTS);
158 }
159
160
161 Vector
162 Tux::get_pos()
163 {
164   float x = tile_pos.x * 32;
165   float y = tile_pos.y * 32;
166
167   switch(direction)
168     {
169     case D_WEST:
170       x -= offset - 32;
171       break;
172     case D_EAST:
173       x += offset - 32;
174       break;
175     case D_NORTH:
176       y -= offset - 32;
177       break;
178     case D_SOUTH:
179       y += offset - 32;
180       break;
181     case D_NONE:
182       break;
183     }
184   
185   return Vector(x, y);
186 }
187
188 void
189 Tux::stop()
190 {
191   offset = 0;
192   direction = D_NONE;
193   input_direction = D_NONE;
194   moving = false;
195 }
196
197 void
198 Tux::set_direction(Direction dir)
199 {
200   input_direction = dir;
201 }
202
203 void 
204 Tux::tryStartWalking() 
205 {
206   if (moving) return;
207   if (input_direction == D_NONE) return;
208
209   WorldMap::Level* level = worldmap->at_level();
210
211   // We got a new direction, so lets start walking when possible
212   Vector next_tile;
213   if ((!level || level->solved) && worldmap->path_ok(input_direction, tile_pos, &next_tile))
214   {
215     tile_pos = next_tile;
216     moving = true;
217     direction = input_direction;
218     back_direction = reverse_dir(direction);
219   }
220   else if (input_direction == back_direction)
221   {
222     moving = true;
223     direction = input_direction;
224     tile_pos = worldmap->get_next_tile(tile_pos, direction);
225     back_direction = reverse_dir(direction);
226   }
227
228 }
229
230 bool 
231 Tux::canWalk(const Tile* tile, Direction dir)
232 {
233   return ((tile->getData() & Tile::WORLDMAP_NORTH && dir == D_NORTH) ||
234           (tile->getData() & Tile::WORLDMAP_SOUTH && dir == D_SOUTH) ||
235           (tile->getData() & Tile::WORLDMAP_EAST && dir == D_EAST) ||
236           (tile->getData() & Tile::WORLDMAP_WEST && dir == D_WEST));
237 }
238
239 void 
240 Tux::tryContinueWalking(float elapsed_time)
241 {
242   if (!moving) return;
243
244   // Let tux walk
245   offset += TUXSPEED * elapsed_time;
246
247   // Do nothing if we have not yet reached the next tile
248   if (offset <= 32) return;
249
250   offset -= 32;
251
252   // if this is a special_tile with passive_message, display it
253   WorldMap::SpecialTile* special_tile = worldmap->at_special_tile();
254   if(special_tile && special_tile->passive_message)
255   {  
256     // direction and the apply_action_ are opposites, since they "see"
257     // directions in a different way
258     if((direction == D_NORTH && special_tile->apply_action_south) ||
259                     (direction == D_SOUTH && special_tile->apply_action_north) ||
260                     (direction == D_WEST && special_tile->apply_action_east) ||
261                     (direction == D_EAST && special_tile->apply_action_west))
262     {
263       worldmap->passive_message = special_tile->map_message;
264       worldmap->passive_message_timer.start(map_message_TIME);
265     }
266   }
267
268   // stop if we reached a level, a WORLDMAP_STOP tile or a special tile without a passive_message
269   if ((worldmap->at_level()) || (worldmap->at(tile_pos)->getData() & Tile::WORLDMAP_STOP) || (special_tile && !special_tile->passive_message))
270   {
271     if(special_tile && !special_tile->map_message.empty() && !special_tile->passive_message) worldmap->passive_message_timer.start(0);
272     stop();
273     return;
274   }
275
276   // if user wants to change direction, try changing, else guess the direction in which to walk next
277   const Tile* tile = worldmap->at(tile_pos);
278   if (direction != input_direction)
279   { 
280     if(canWalk(tile, input_direction))
281     {  
282       direction = input_direction;
283       back_direction = reverse_dir(direction);
284     }
285   }
286   else
287   {
288     Direction dir = D_NONE;
289     if (tile->getData() & Tile::WORLDMAP_NORTH && back_direction != D_NORTH) dir = D_NORTH;
290     else if (tile->getData() & Tile::WORLDMAP_SOUTH && back_direction != D_SOUTH) dir = D_SOUTH;
291     else if (tile->getData() & Tile::WORLDMAP_EAST && back_direction != D_EAST) dir = D_EAST;
292     else if (tile->getData() & Tile::WORLDMAP_WEST && back_direction != D_WEST) dir = D_WEST;
293
294     if (dir == D_NONE) 
295     {
296       // Should never be reached if tiledata is good
297       msg_warning << "Could not determine where to walk next" << std::endl;
298       stop();
299       return;
300     }
301
302     direction = dir;
303     input_direction = direction;
304     back_direction = reverse_dir(direction);
305   }
306
307   // Walk automatically to the next tile
308   if(direction != D_NONE)
309   {
310     Vector next_tile;
311     if (worldmap->path_ok(direction, tile_pos, &next_tile))
312     {
313       tile_pos = next_tile;
314     }
315     else
316     {
317       msg_warning << "Tilemap data is buggy" << std::endl;
318       stop();
319     }
320   }
321 }
322
323 void
324 Tux::updateInputDirection()
325 {
326   if(main_controller->hold(Controller::UP)) input_direction = D_NORTH;
327   else if(main_controller->hold(Controller::DOWN)) input_direction = D_SOUTH;
328   else if(main_controller->hold(Controller::LEFT)) input_direction = D_WEST;
329   else if(main_controller->hold(Controller::RIGHT)) input_direction = D_EAST;
330 }
331
332
333 void
334 Tux::update(float elapsed_time)
335 {
336   updateInputDirection(); 
337   if (moving) tryContinueWalking(elapsed_time); else tryStartWalking();
338 }
339
340 //---------------------------------------------------------------------------
341
342 WorldMap::WorldMap()
343   : tux(0), solids(0)
344 {
345   tile_manager = new TileManager("images/worldmap.strf");
346   
347   tux = new Tux(this);
348   add_object(tux);
349     
350   messagedot = new Surface("images/worldmap/common/messagedot.png");
351   teleporterdot = sprite_manager->create("images/worldmap/common/teleporter.sprite");
352
353   name = "<no title>";
354   music = "music/salcon.ogg";
355   intro_displayed = false;
356
357   total_stats.reset();
358 }
359
360 WorldMap::~WorldMap()
361 {
362   clear_objects();
363   for(SpawnPoints::iterator i = spawn_points.begin();
364       i != spawn_points.end(); ++i) {
365     delete *i;
366   }
367   for(Levels::iterator i = levels.begin(); i != levels.end(); ++i) {
368     Level& level = *i;
369     delete level.sprite;
370   }
371   for(SpecialTiles::iterator i = special_tiles.begin(); i != special_tiles.end(); ++i) {
372     delete i->sprite;
373   }
374   
375   delete tile_manager;
376
377   delete messagedot;
378   delete teleporterdot;
379 }
380
381 void
382 WorldMap::add_object(GameObject* object)
383 {
384   TileMap* tilemap = dynamic_cast<TileMap*> (object);
385   if(tilemap != 0 && tilemap->is_solid()) {
386     solids = tilemap;
387   }
388
389   game_objects.push_back(object);
390 }
391
392 void
393 WorldMap::clear_objects()
394 {
395   for(GameObjects::iterator i = game_objects.begin();
396       i != game_objects.end(); ++i)
397     delete *i;
398   game_objects.clear();
399   solids = 0;
400   tux = new Tux(this);
401   add_object(tux);
402 }
403
404 // Don't forget to set map_filename before calling this
405 void
406 WorldMap::load_map()
407 {
408   levels_path = FileSystem::dirname(map_filename);
409
410   try {
411     lisp::Parser parser;
412     std::auto_ptr<lisp::Lisp> root (parser.parse(map_filename));
413
414     const lisp::Lisp* lisp = root->get_lisp("supertux-level");
415     if(!lisp)
416       throw std::runtime_error("file isn't a supertux-level file.");
417
418     lisp->get("name", name);
419     
420     const lisp::Lisp* sector = lisp->get_lisp("sector");
421     if(!sector)
422       throw std::runtime_error("No sector sepcified in worldmap file.");
423     
424     clear_objects();
425     lisp::ListIterator iter(sector);
426     while(iter.next()) {
427       if(iter.item() == "tilemap") {
428         add_object(new TileMap(*(iter.lisp()), tile_manager));
429       } else if(iter.item() == "background") {
430         add_object(new Background(*(iter.lisp())));
431       } else if(iter.item() == "music") {
432         iter.value()->get(music);
433       } else if(iter.item() == "intro-script") {
434         iter.value()->get(intro_script);
435       } else if(iter.item() == "worldmap-spawnpoint") {
436         SpawnPoint* sp = new SpawnPoint(iter.lisp());
437         spawn_points.push_back(sp);
438       } else if(iter.item() == "level") {
439         parse_level_tile(iter.lisp());
440       } else if(iter.item() == "special-tile") {
441         parse_special_tile(iter.lisp());
442       } else if(iter.item() == "name") {
443         // skip
444       } else {
445         msg_warning << "Unknown token '" << iter.item() << "' in worldmap" << std::endl;
446       }
447     }
448     if(solids == 0)
449       throw std::runtime_error("No solid tilemap specified");
450
451     // search for main spawnpoint
452     for(SpawnPoints::iterator i = spawn_points.begin();
453         i != spawn_points.end(); ++i) {
454       SpawnPoint* sp = *i;
455       if(sp->name == "main") {
456         Vector p = sp->pos;
457         tux->set_tile_pos(p);
458         break;
459       }
460     }
461
462   } catch(std::exception& e) {
463     std::stringstream msg;
464     msg << "Problem when parsing worldmap '" << map_filename << "': " <<
465       e.what();
466     throw std::runtime_error(msg.str());
467   }
468 }
469
470 void
471 WorldMap::parse_special_tile(const lisp::Lisp* lisp)
472 {
473   SpecialTile special_tile;
474   
475   lisp->get("x", special_tile.pos.x);
476   lisp->get("y", special_tile.pos.y);
477
478   std::string sprite;
479   if (lisp->get("sprite", sprite)) {
480     special_tile.sprite = sprite_manager->create(sprite);
481   } else {
482     special_tile.sprite = 0;
483   }
484
485   lisp->get("map-message", special_tile.map_message);
486   special_tile.passive_message = false;
487   lisp->get("passive-message", special_tile.passive_message);
488   special_tile.teleport_dest = Vector(-1,-1);
489   lisp->get("teleport-to-x", special_tile.teleport_dest.x);
490   lisp->get("teleport-to-y", special_tile.teleport_dest.y);
491   special_tile.invisible = false;
492   lisp->get("invisible-tile", special_tile.invisible);
493
494   special_tile.apply_action_north = true;
495   special_tile.apply_action_south = true;
496   special_tile.apply_action_east = true;
497   special_tile.apply_action_west = true;
498
499   std::string apply_direction;
500   lisp->get("apply-to-direction", apply_direction);
501   if(!apply_direction.empty()) {
502     special_tile.apply_action_north = false;
503     special_tile.apply_action_south = false;
504     special_tile.apply_action_east = false;
505     special_tile.apply_action_west = false;
506     if(apply_direction.find("north") != std::string::npos)
507       special_tile.apply_action_north = true;
508     if(apply_direction.find("south") != std::string::npos)
509       special_tile.apply_action_south = true;
510     if(apply_direction.find("east") != std::string::npos)
511       special_tile.apply_action_east = true;
512     if(apply_direction.find("west") != std::string::npos)
513       special_tile.apply_action_west = true;
514   }
515   
516   special_tiles.push_back(special_tile);
517 }
518
519 void
520 WorldMap::parse_level_tile(const lisp::Lisp* level_lisp)
521 {
522   Level level;
523
524   level.solved = false;
525                   
526   level.north = true;
527   level.east  = true;
528   level.south = true;
529   level.west  = true;
530
531   std::string sprite = "images/worldmap/common/leveldot.sprite";
532   level_lisp->get("sprite", sprite);
533   level.sprite = sprite_manager->create(sprite);
534
535   level_lisp->get("extro-script", level.extro_script);
536   level_lisp->get("next-worldmap", level.next_worldmap);
537
538   level.quit_worldmap = false;
539   level_lisp->get("quit-worldmap", level.quit_worldmap);
540
541   level_lisp->get("name", level.name);
542   
543   if (!PHYSFS_exists((levels_path + level.name).c_str()))
544   {
545         // Do we want to bail out instead...? We might get messages from modders
546         // who can't make their levels run because they're too dumb to watch
547         // their terminals...
548     msg_warning << "level file '" << level.name << "' does not exist and will not be added to the worldmap" << std::endl;
549     return;
550   }
551
552   level_lisp->get("x", level.pos.x);
553   level_lisp->get("y", level.pos.y);
554
555   level.auto_path = true;
556   level_lisp->get("auto-path", level.auto_path);
557
558   level.vertical_flip = false;
559   level_lisp->get("vertical-flip", level.vertical_flip);
560
561   levels.push_back(level);
562 }
563
564 void
565 WorldMap::get_level_title(Level& level)
566 {
567   /** get special_tile's title */
568   level.title = "<no title>";
569
570   try {
571     lisp::Parser parser;
572     std::auto_ptr<lisp::Lisp> root (parser.parse(levels_path + level.name));
573
574     const lisp::Lisp* level_lisp = root->get_lisp("supertux-level");
575     if(!level_lisp)
576       return;
577     
578     level_lisp->get("name", level.title);
579   } catch(std::exception& e) {
580     msg_warning << "Problem when reading leveltitle: " << e.what() << std::endl;
581     return;
582   }
583 }
584
585 void WorldMap::calculate_total_stats()
586 {
587   total_stats.reset();
588   for(Levels::iterator i = levels.begin(); i != levels.end(); ++i)
589     {
590     if (i->solved)
591       {
592       total_stats += i->statistics;
593       }
594     }
595 }
596
597 void
598 WorldMap::on_escape_press()
599 {
600   // Show or hide the menu
601   if(!Menu::current()) {
602     Menu::set_current(worldmap_menu);
603     tux->set_direction(D_NONE);  // stop tux movement when menu is called
604   } else {
605     Menu::set_current(0);
606   }
607 }
608
609 Vector
610 WorldMap::get_next_tile(Vector pos, Direction direction)
611 {
612   switch(direction) {
613     case D_WEST:
614       pos.x -= 1;
615       break;
616     case D_EAST:
617       pos.x += 1;
618       break;
619     case D_NORTH:
620       pos.y -= 1;
621       break;
622     case D_SOUTH:
623       pos.y += 1;
624       break;
625     case D_NONE:
626       break;
627   }
628   return pos;
629 }
630
631 bool
632 WorldMap::path_ok(Direction direction, Vector old_pos, Vector* new_pos)
633 {
634   *new_pos = get_next_tile(old_pos, direction);
635
636   if (!(new_pos->x >= 0 && new_pos->x < solids->get_width()
637         && new_pos->y >= 0 && new_pos->y < solids->get_height()))
638     { // New position is outsite the tilemap
639       return false;
640     }
641   else
642     { // Check if the tile allows us to go to new_pos
643       switch(direction)
644         {
645         case D_WEST:
646           return (at(old_pos)->getData() & Tile::WORLDMAP_WEST
647               && at(*new_pos)->getData() & Tile::WORLDMAP_EAST);
648
649         case D_EAST:
650           return (at(old_pos)->getData() & Tile::WORLDMAP_EAST
651               && at(*new_pos)->getData() & Tile::WORLDMAP_WEST);
652
653         case D_NORTH:
654           return (at(old_pos)->getData() & Tile::WORLDMAP_NORTH
655               && at(*new_pos)->getData() & Tile::WORLDMAP_SOUTH);
656
657         case D_SOUTH:
658           return (at(old_pos)->getData() & Tile::WORLDMAP_SOUTH
659               && at(*new_pos)->getData() & Tile::WORLDMAP_NORTH);
660
661         case D_NONE:
662           assert(!"path_ok() can't work if direction is NONE");
663         }
664       return false;
665     }
666 }
667
668 void
669 WorldMap::finished_level(const std::string& filename)
670 {
671   // TODO calculate level from filename?
672   (void) filename;
673   Level* level = at_level();
674
675   bool old_level_state = level->solved;
676   level->solved = true;
677   level->sprite->set_action("solved");
678
679   // deal with statistics
680   level->statistics.merge(global_stats);
681   calculate_total_stats();
682
683   if(savegame_file != "")
684     savegame(savegame_file);
685
686   if (old_level_state != level->solved && level->auto_path) {
687     // Try to detect the next direction to which we should walk
688     // FIXME: Mostly a hack
689     Direction dir = D_NONE;
690   
691     const Tile* tile = at(tux->get_tile_pos());
692
693     if (tile->getData() & Tile::WORLDMAP_NORTH
694         && tux->back_direction != D_NORTH)
695       dir = D_NORTH;
696     else if (tile->getData() & Tile::WORLDMAP_SOUTH
697         && tux->back_direction != D_SOUTH)
698       dir = D_SOUTH;
699     else if (tile->getData() & Tile::WORLDMAP_EAST
700         && tux->back_direction != D_EAST)
701       dir = D_EAST;
702     else if (tile->getData() & Tile::WORLDMAP_WEST
703         && tux->back_direction != D_WEST)
704       dir = D_WEST;
705
706     if (dir != D_NONE) {
707       tux->set_direction(dir);
708     }
709   }
710
711   if (level->extro_script != "") {
712     /* TODO
713     try {
714       std::auto_ptr<ScriptInterpreter> interpreter
715         (new ScriptInterpreter(levels_path));
716       std::istringstream in(level->extro_script);
717       interpreter->run_script(in, "level-extro-script");
718       add_object(interpreter.release());
719     } catch(std::exception& e) {
720       msg_fatal << "Couldn't run level-extro-script:" << e.what() << std::endl;
721     }
722     */
723   }
724   
725   if (!level->next_worldmap.empty()) {
726     // Load given worldmap
727     loadmap(level->next_worldmap);
728   }
729   
730   if (level->quit_worldmap)
731     main_loop->exit_screen();
732 }
733
734 void
735 WorldMap::update(float delta)
736 {
737   Menu* menu = Menu::current();
738   if(menu) {
739     menu->update();
740
741     if(menu == worldmap_menu) {
742       switch (worldmap_menu->check())
743       {
744         case MNID_RETURNWORLDMAP: // Return to game  
745           Menu::set_current(0);
746           break;
747         case MNID_QUITWORLDMAP: // Quit Worldmap
748           main_loop->exit_screen();
749           break;
750       }
751     } else if(menu == options_menu) {
752       process_options_menu();
753     }
754
755     return;
756   }
757
758   // update GameObjects
759   for(GameObjects::iterator i = game_objects.begin();
760       i != game_objects.end(); ++i) {
761     GameObject* object = *i;
762     object->update(delta);
763   }
764
765   // remove old GameObjects
766   for(GameObjects::iterator i = game_objects.begin();
767       i != game_objects.end(); ) {
768     GameObject* object = *i;
769     if(!object->is_valid()) {
770       delete object;
771       i = game_objects.erase(i);
772     } else {
773       ++i;
774     }
775   }
776
777   // position "camera"
778   Vector tux_pos = tux->get_pos();
779   camera_offset.x = tux_pos.x - SCREEN_WIDTH/2;
780   camera_offset.y = tux_pos.y - SCREEN_HEIGHT/2;
781
782   if (camera_offset.x < 0)
783     camera_offset.x = 0;
784   if (camera_offset.y < 0)
785     camera_offset.y = 0;
786
787   if (camera_offset.x > solids->get_width()*32 - SCREEN_WIDTH)
788     camera_offset.x = solids->get_width()*32 - SCREEN_WIDTH;
789   if (camera_offset.y > solids->get_height()*32 - SCREEN_HEIGHT)
790     camera_offset.y = solids->get_height()*32 - SCREEN_HEIGHT;
791
792   // handle input
793   bool enter_level = false;
794   if(main_controller->pressed(Controller::ACTION)
795       || main_controller->pressed(Controller::JUMP)
796       || main_controller->pressed(Controller::MENU_SELECT))
797     enter_level = true;
798   if(main_controller->pressed(Controller::PAUSE_MENU))
799     on_escape_press();
800   
801   if (enter_level && !tux->is_moving())
802     {
803       /* Check special tile action */
804       SpecialTile* special_tile = at_special_tile();
805       if(special_tile)
806         {
807         if (special_tile->teleport_dest != Vector(-1,-1))
808           {
809           // TODO: an animation, camera scrolling or a fading would be a nice touch
810           sound_manager->play("sounds/warp.wav");
811           tux->back_direction = D_NONE;
812           tux->set_tile_pos(special_tile->teleport_dest);
813           SDL_Delay(1000);
814           }
815         }
816
817       /* Check level action */
818       Level* level = at_level();
819       if (!level) {
820         msg_warning << "No level to enter at: " << tux->get_tile_pos().x << ", " << tux->get_tile_pos().y << std::endl;
821         return;
822       }
823
824       if (level->pos == tux->get_tile_pos()) {
825         // do a shriking fade to the level
826         shrink_fade(Vector((level->pos.x*32 + 16 + offset.x),
827                            (level->pos.y*32 + 16 + offset.y)), 500);
828
829         try {
830           GameSession *session =
831             new GameSession(levels_path + level->name,
832                 ST_GL_LOAD_LEVEL_FILE, &level->statistics);
833           main_loop->push_screen(session);
834         } catch(std::exception& e) {
835           msg_fatal << "Couldn't load level: " << e.what() << std::endl;
836         }
837       }
838     }
839   else
840     {
841 //      tux->set_direction(input_direction);
842     }
843 }
844
845 const Tile*
846 WorldMap::at(Vector p)
847 {
848   return solids->get_tile((int) p.x, (int) p.y);
849 }
850
851 WorldMap::Level*
852 WorldMap::at_level()
853 {
854   for(Levels::iterator i = levels.begin(); i != levels.end(); ++i)
855     {
856       if (i->pos == tux->get_tile_pos())
857         return &*i; 
858     }
859
860   return 0;
861 }
862
863 WorldMap::SpecialTile*
864 WorldMap::at_special_tile()
865 {
866   for(SpecialTiles::iterator i = special_tiles.begin(); i != special_tiles.end(); ++i)
867     {
868       if (i->pos == tux->get_tile_pos())
869         return &*i; 
870     }
871
872   return 0;
873 }
874
875 void
876 WorldMap::draw(DrawingContext& context)
877 {
878   context.push_transform();
879   context.set_translation(camera_offset);
880   
881   for(GameObjects::iterator i = game_objects.begin();
882       i != game_objects.end(); ++i) {
883     GameObject* object = *i;
884     object->draw(context);
885   }
886   
887   for(Levels::iterator i = levels.begin(); i != levels.end(); ++i)
888     {
889       const Level& level = *i;
890       level.sprite->draw(context, level.pos*32 + Vector(16, 16), LAYER_TILES+1);
891     }
892
893   for(SpecialTiles::iterator i = special_tiles.begin(); i != special_tiles.end(); ++i)
894     {
895       if(i->invisible)
896         continue;
897
898       if (i->sprite)
899         i->sprite->draw(context, i->pos*32 + Vector(16, 16), LAYER_TILES+1);
900
901       else if (i->teleport_dest != Vector(-1, -1))
902         teleporterdot->draw(context, i->pos*32 + Vector(16, 16), LAYER_TILES+1);
903
904       else if (!i->map_message.empty() && !i->passive_message)
905         context.draw_surface(messagedot,
906                 Vector(i->pos.x*32, i->pos.y*32), LAYER_TILES+1);
907     }
908
909   draw_status(context);
910   context.pop_transform();
911 }
912
913 void
914 WorldMap::draw_status(DrawingContext& context)
915 {
916   context.push_transform();
917   context.set_translation(Vector(0, 0));
918  
919   player_status->draw(context);
920
921   if (!tux->is_moving())
922     {
923       for(Levels::iterator i = levels.begin(); i != levels.end(); ++i)
924         {
925           if (i->pos == tux->get_tile_pos())
926             {
927               if(i->title == "")
928                 get_level_title(*i);
929
930               context.draw_text(white_text, i->title, 
931                   Vector(SCREEN_WIDTH/2,
932                          SCREEN_HEIGHT - white_text->get_height() - 30),
933                   CENTER_ALLIGN, LAYER_FOREGROUND1);
934
935               i->statistics.draw_worldmap_info(context);
936               break;
937             }
938         }
939       for(SpecialTiles::iterator i = special_tiles.begin(); i != special_tiles.end(); ++i)
940         {
941           if (i->pos == tux->get_tile_pos())
942             {
943                /* Display an in-map message in the map, if any as been selected */
944               if(!i->map_message.empty() && !i->passive_message)
945                 context.draw_text(gold_text, i->map_message, 
946                     Vector(SCREEN_WIDTH/2,
947                            SCREEN_HEIGHT - white_text->get_height() - 60),
948                     CENTER_ALLIGN, LAYER_FOREGROUND1);
949               break;
950             }
951         }
952     }
953   /* Display a passive message in the map, if needed */
954   if(passive_message_timer.started())
955     context.draw_text(gold_text, passive_message, 
956             Vector(SCREEN_WIDTH/2, SCREEN_HEIGHT - white_text->get_height() - 60),
957             CENTER_ALLIGN, LAYER_FOREGROUND1);
958
959   context.pop_transform();
960 }
961
962 void
963 WorldMap::setup()
964 {
965   sound_manager->play_music(music);
966   Menu::set_current(NULL);
967
968   current_ = this;
969 }
970
971 void
972 WorldMap::savegame(const std::string& filename)
973 {
974   if(filename == "")
975     return;
976
977   std::string dir = FileSystem::dirname(filename);
978   if(PHYSFS_exists(dir.c_str()) == 0 && PHYSFS_mkdir(dir.c_str()) != 0) {
979     std::ostringstream msg;
980     msg << "Couldn't create directory '" << dir << "' for savegame:"
981         << PHYSFS_getLastError();
982     throw std::runtime_error(msg.str());
983   }
984   if(!PHYSFS_isDirectory(dir.c_str())) {
985     std::ostringstream msg;
986     msg << "'" << dir << "' is not a directory.";
987     throw std::runtime_error(msg.str());
988   }
989   
990   lisp::Writer writer(filename);
991
992   int nb_solved_levels = 0, total_levels = 0;
993   for(Levels::iterator i = levels.begin(); i != levels.end(); ++i) {
994     ++total_levels;
995     if (i->solved)
996       ++nb_solved_levels;
997   }
998   char nb_solved_levels_str[80], total_levels_str[80];
999   sprintf(nb_solved_levels_str, "%d", nb_solved_levels);
1000   sprintf(total_levels_str, "%d", total_levels);
1001
1002   writer.write_comment("Worldmap save file");
1003
1004   writer.start_list("supertux-savegame");
1005
1006   writer.write_int("version", 1);
1007   writer.write_string("title",
1008       std::string(name + " - " + nb_solved_levels_str+"/"+total_levels_str));
1009   writer.write_string("map", map_filename);
1010   writer.write_bool("intro-displayed", intro_displayed);
1011
1012   writer.start_list("tux");
1013
1014   writer.write_float("x", tux->get_tile_pos().x);
1015   writer.write_float("y", tux->get_tile_pos().y);
1016   writer.write_string("back", direction_to_string(tux->back_direction));
1017   player_status->write(writer);
1018   writer.write_string("back", direction_to_string(tux->back_direction));
1019
1020   writer.end_list("tux");
1021
1022   writer.start_list("levels");
1023
1024   for(Levels::iterator i = levels.begin(); i != levels.end(); ++i)
1025     {
1026       if (i->solved)
1027         {
1028         writer.start_list("level");
1029
1030         writer.write_string("name", i->name);
1031         writer.write_bool("solved", true);
1032         i->statistics.write(writer);
1033
1034         writer.end_list("level");
1035         }
1036     }  
1037
1038   writer.end_list("levels");
1039
1040   writer.end_list("supertux-savegame");
1041 }
1042
1043 void
1044 WorldMap::loadgame(const std::string& filename)
1045 {
1046   msg_debug << "loadgame: " << filename << std::endl;
1047   savegame_file = filename;
1048   
1049   if (PHYSFS_exists(filename.c_str())) // savegame exists
1050   {
1051     try {
1052       lisp::Parser parser;
1053       
1054       std::auto_ptr<lisp::Lisp> root (parser.parse(filename));
1055     
1056       const lisp::Lisp* savegame = root->get_lisp("supertux-savegame");
1057       if(!savegame)
1058         throw std::runtime_error("File is not a supertux-savegame file.");
1059
1060       /* Get the Map filename and then load it before setting level settings */
1061       std::string cur_map_filename = map_filename;
1062       savegame->get("map", map_filename);
1063       load_map(); 
1064
1065       savegame->get("intro-displayed", intro_displayed);
1066
1067       const lisp::Lisp* tux_lisp = savegame->get_lisp("tux");
1068       if(tux)
1069       {
1070         Vector p;
1071         std::string back_str = "none";
1072
1073         tux_lisp->get("x", p.x);
1074         tux_lisp->get("y", p.y);
1075         tux_lisp->get("back", back_str);
1076         player_status->read(*tux_lisp);
1077         if (player_status->coins < 0) player_status->reset();
1078         tux->back_direction = string_to_direction(back_str);      
1079         tux->set_tile_pos(p);
1080       }
1081
1082       const lisp::Lisp* levels_lisp = savegame->get_lisp("levels");
1083       if(levels_lisp) {
1084         lisp::ListIterator iter(levels_lisp);
1085         while(iter.next()) {
1086           if(iter.item() == "level") {
1087             std::string name;
1088             bool solved = false;
1089
1090             const lisp::Lisp* level = iter.lisp();
1091             level->get("name", name);
1092             level->get("solved", solved);
1093
1094             for(Levels::iterator i = levels.begin(); i != levels.end(); ++i)
1095             {
1096               if (name == i->name)
1097               {
1098                 i->solved = solved;
1099                 i->sprite->set_action(solved ? "solved" : "default");
1100                 i->statistics.parse(*level);
1101                 break;
1102               }
1103             }
1104           } else {
1105             msg_warning << "Unknown token '" << iter.item() << "' in levels block in worldmap" << std::endl;
1106           }
1107         }
1108       }
1109     } catch(std::exception& e) {
1110       msg_warning << "Problem loading game '" << filename << "': " << e.what() << std::endl;
1111       load_map();
1112       player_status->reset();
1113     }
1114   }
1115   else
1116   {
1117     load_map();
1118     player_status->reset();
1119   }
1120
1121   calculate_total_stats();
1122 }
1123
1124 void
1125 WorldMap::loadmap(const std::string& filename)
1126 {
1127   savegame_file = "";
1128   map_filename = filename;
1129   load_map();
1130 }
1131
1132 } // namespace WorldMapNS