-Some cleanups in text scrolling code
[supertux.git] / src / worldmap.cpp
index 360cc3c..59da5f2 100644 (file)
 //  You should have received a copy of the GNU General Public License
 //  along with this program; if not, write to the Free Software
 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#include <config.h>
 
 #include <iostream>
 #include <fstream>
 #include <vector>
 #include <cassert>
+#include <stdexcept>
+#include <sstream>
 #include <unistd.h>
 
 #include "app/globals.h"
+#include "app/gettext.h"
+#include "app/setup.h"
 #include "video/surface.h"
 #include "video/screen.h"
 #include "video/drawing_context.h"
-#include "utils/lispreader.h"
-#include "utils/lispwriter.h"
 #include "special/frame_rate.h"
+#include "audio/sound_manager.h"
+#include "lisp/parser.h"
+#include "lisp/lisp.h"
+#include "lisp/list_iterator.h"
+#include "lisp/writer.h"
 #include "gameloop.h"
-#include "app/setup.h"
 #include "sector.h"
 #include "worldmap.h"
-#include "audio/sound_manager.h"
 #include "resources.h"
-#include "app/gettext.h"
 #include "misc.h"
+#include "player_status.h"
+#include "textscroller.h"
 
-#define map_message_TIME 2800
+#define map_message_TIME 2.8
 
 Menu* worldmap_menu  = 0;
 
+static const float TUXSPEED = 200;
+
 namespace WorldMapNS {
 
 Direction reverse_dir(Direction direction)
@@ -96,124 +105,6 @@ string_to_direction(const std::string& directory)
     return D_NONE;
 }
 
-TileManager::TileManager()
-{
-  std::string stwt_filename = datadir +  "/images/worldmap/antarctica.stwt";
-  lisp_object_t* root_obj = lisp_read_from_file(stwt_filename);
-  if (!root_obj)
-    Termination::abort("Couldn't load file", stwt_filename);
-
-  if (strcmp(lisp_symbol(lisp_car(root_obj)), "supertux-worldmap-tiles") == 0)
-    {
-      lisp_object_t* cur = lisp_cdr(root_obj);
-
-      while(!lisp_nil_p(cur))
-        {
-          lisp_object_t* element = lisp_car(cur);
-
-          if (strcmp(lisp_symbol(lisp_car(element)), "tile") == 0)
-            {
-              int id = 0;
-
-              Tile* tile = new Tile;             
-              tile->north = tile->east = tile->south = tile->west = true;
-              tile->stop  = true;
-              tile->auto_walk = false;
-
-              LispReader reader(lisp_cdr(element));
-              reader.read_int("id", id);
-
-              std::string temp;
-              reader.read_string("possible-directions", temp);
-              if(!temp.empty())
-                {
-                tile->north = tile->east = tile->south = tile->west = false;
-                if(temp.find("north") != std::string::npos)
-                  tile->north = true;
-                if(temp.find("south") != std::string::npos)
-                  tile->south = true;
-                if(temp.find("east") != std::string::npos)
-                  tile->east = true;
-                if(temp.find("west") != std::string::npos)
-                  tile->west = true;
-                }
-
-              /* For backward compatibility */
-              reader.read_bool("north", tile->north);
-              reader.read_bool("south", tile->south);
-              reader.read_bool("west",  tile->west);
-              reader.read_bool("east",  tile->east);
-
-              reader.read_bool("stop",  tile->stop);
-              reader.read_bool("auto-walk",  tile->auto_walk);
-
-              reader.read_string("one-way", temp);
-              tile->one_way = BOTH_WAYS;
-              if(!temp.empty())
-                {
-                if(temp == "north-south")
-                  tile->one_way = NORTH_SOUTH_WAY;
-                else if(temp == "south-north")
-                  tile->one_way = SOUTH_NORTH_WAY;
-                else if(temp == "east-west")
-                  tile->one_way = EAST_WEST_WAY;
-                else if(temp == "west-east")
-                  tile->one_way = WEST_EAST_WAY;
-                }
-
-              std::vector<std::string> filenames;
-              reader.read_string_vector("image", filenames);
-
-              if(filenames.size() == 0)
-                std::cerr << "Warning: no image specified for tile " << id
-                          << ".\nIgnoring...\n" << std::endl;
-
-              for(int i = 0; static_cast<unsigned int>(i) < filenames.size(); i++)
-                {
-                Surface* image = new Surface(
-                         datadir +  "/images/worldmap/" + filenames[i], true);
-                tile->images.push_back(image);
-                }
-
-              tile->anim_speed = 25;
-              reader.read_int("anim-speed", tile->anim_speed);
-
-
-              if (id >= int(tiles.size()))
-                tiles.resize(id+1);
-
-              tiles[id] = tile;
-            }
-          else
-            {
-              puts("Unhandled symbol");
-            }
-
-          cur = lisp_cdr(cur);
-        }
-    }
-  else
-    {
-      assert(0);
-    }
-
-  lisp_free(root_obj);
-}
-
-TileManager::~TileManager()
-{
-  for(std::vector<Tile*>::iterator i = tiles.begin(); i != tiles.end(); ++i)
-    delete *i;
-}
-
-Tile*
-TileManager::get(int i)
-{
-  assert(i >=0 && i < int(tiles.size()));
-  return tiles[i];
-}
-
 //---------------------------------------------------------------------------
 
 Tux::Tux(WorldMap* worldmap_)
@@ -332,8 +223,8 @@ Tux::action(float delta)
     }
   else
     {
-      // Let tux walk a few pixels (20 pixel/sec)
-      offset += 20.0f * delta;
+      // Let tux walk
+      offset += TUXSPEED * delta;
 
       if (offset > 32)
         { // We reached the next tile, so we check what to do now
@@ -353,62 +244,71 @@ Tux::action(float delta)
               }
             }
 
-          if (worldmap->at(tile_pos)->stop ||
+          if (worldmap->at(tile_pos)->getData() & Tile::WORLDMAP_STOP ||
              (special_tile && !special_tile->passive_message) ||
               worldmap->at_level())
             {
               if(special_tile && !special_tile->map_message.empty() &&
                 !special_tile->passive_message)
-                worldmap->passive_message_timer.stop();
+                worldmap->passive_message_timer.start(0);
               stop();
             }
           else
             {
-              if (worldmap->at(tile_pos)->auto_walk || direction != input_direction)
-                { // Turn to a new direction
-                  Tile* tile = worldmap->at(tile_pos);
-
-                  if(direction != input_direction && 
-                     ((tile->north && input_direction == D_NORTH) ||
-                     (tile->south && input_direction == D_SOUTH) ||
-                     (tile->east && input_direction == D_EAST) ||
-                     (tile->west && input_direction == D_WEST)))
+              const Tile* tile = worldmap->at(tile_pos);
+              if (direction != input_direction)
+                { 
+                  // Turn to a new direction
+                  const Tile* tile = worldmap->at(tile_pos);
+
+                  if((tile->getData() & Tile::WORLDMAP_NORTH 
+                      && input_direction == D_NORTH) ||
+                     (tile->getData() & Tile::WORLDMAP_SOUTH
+                      && input_direction == D_SOUTH) ||
+                     (tile->getData() & Tile::WORLDMAP_EAST
+                      && input_direction == D_EAST) ||
+                     (tile->getData() & Tile::WORLDMAP_WEST
+                      && input_direction == D_WEST))
                     {  // player has changed direction during auto-movement
-                    direction = input_direction;
-                    back_direction = reverse_dir(direction);
-                    }
-                  else if(direction != input_direction)
-                    {  // player has changed to impossible tile
+                      direction = input_direction;
                       back_direction = reverse_dir(direction);
-                      stop();
                     }
                   else
-                    {
-                    Direction dir = D_NONE;
-                  
-                    if (tile->north && back_direction != D_NORTH)
-                      dir = D_NORTH;
-                    else if (tile->south && back_direction != D_SOUTH)
-                      dir = D_SOUTH;
-                    else if (tile->east && back_direction != D_EAST)
-                      dir = D_EAST;
-                    else if (tile->west && back_direction != D_WEST)
-                      dir = D_WEST;
-
-                    if (dir != D_NONE)
-                      {
-                      direction = dir;
-                      input_direction = direction;
+                    {  // player has changed to impossible tile
                       back_direction = reverse_dir(direction);
-                      }
-                    else
-                      {
-                      // Should never be reached if tiledata is good
                       stop();
-                      return;
-                      }
                     }
+                }
+              else
+                {
+                Direction dir = D_NONE;
+              
+                if (tile->getData() & Tile::WORLDMAP_NORTH
+                    && back_direction != D_NORTH)
+                  dir = D_NORTH;
+                else if (tile->getData() & Tile::WORLDMAP_SOUTH
+                    && back_direction != D_SOUTH)
+                  dir = D_SOUTH;
+                else if (tile->getData() & Tile::WORLDMAP_EAST
+                    && back_direction != D_EAST)
+                  dir = D_EAST;
+                else if (tile->getData() & Tile::WORLDMAP_WEST
+                    && back_direction != D_WEST)
+                  dir = D_WEST;
+
+                if (dir != D_NONE)
+                  {
+                  direction = dir;
+                  input_direction = direction;
+                  back_direction = reverse_dir(direction);
                   }
+                else
+                  {
+                  // Should never be reached if tiledata is good
+                  stop();
+                  return;
+                  }
+                }
 
               // Walk automatically to the next tile
               if(direction != D_NONE)
@@ -430,44 +330,10 @@ Tux::action(float delta)
 }
 
 //---------------------------------------------------------------------------
-Tile::Tile()
-{
-}
-
-Tile::~Tile()
-{
-  for(std::vector<Surface*>::iterator i = images.begin(); i != images.end(); i++)
-    delete *i;
-}
-
-
-void
-Tile::draw(DrawingContext& context, Vector pos)
-{
-  // same code as from tile_manager.cpp draw_tile()
-
-  if(!images.size())
-    return;
-
-  if(images.size() > 1)
-    {
-    size_t frame 
-      = ((global_frame_counter*25) / anim_speed) % images.size();
-
-    context.draw_surface(images[frame], pos, LAYER_TILES);
-    }
-  else if (images.size() == 1)
-    {
-    context.draw_surface(images[0], pos, LAYER_TILES);
-    }
-}
-
-//---------------------------------------------------------------------------
 
 WorldMap::WorldMap()
 {
-  tile_manager = new TileManager();
-  //tux = new Tux(this);
+  tile_manager = new TileManager("images/worldmap/antarctica.stwt");
   
   width  = 20;
   height = 15;
@@ -485,10 +351,8 @@ WorldMap::WorldMap()
   enter_level = false;
 
   name = "<no title>";
-  music = "SALCON.MOD";
-
-  global_frame_counter = 0;
-  frame_timer.init(true);
+  music = "salcon.mod";
+  intro_displayed = false;
 
   total_stats.reset();
 }
@@ -508,148 +372,149 @@ WorldMap::~WorldMap()
 void
 WorldMap::load_map()
 {
-  lisp_object_t* root_obj = lisp_read_from_file(datadir + "/levels/worldmap/" + map_filename);
-  if (!root_obj)
-    Termination::abort("Couldn't load file", datadir + "/levels/worldmap/" + map_filename);
-
-  if (strcmp(lisp_symbol(lisp_car(root_obj)), "supertux-worldmap") == 0)
-    {
-      lisp_object_t* cur = lisp_cdr(root_obj);
-
-      while(!lisp_nil_p(cur))
-        {
-          lisp_object_t* element = lisp_car(cur);
+  levels_path = FileSystem::dirname(map_filename);
+
+  try {
+    lisp::Parser parser;
+    std::string filename = get_resource_filename(map_filename);
+    std::auto_ptr<lisp::Lisp> root (parser.parse(filename));
+
+    const lisp::Lisp* lisp = root->get_lisp("supertux-worldmap");
+    if(!lisp)
+      throw new std::runtime_error("file isn't a supertux-worldmap file.");
+
+    lisp::ListIterator iter(lisp);
+    while(iter.next()) {
+      if(iter.item() == "tilemap") {
+        if(tilemap.size() > 0)
+          throw new std::runtime_error("multiple tilemaps specified");
+        
+        const lisp::Lisp* tilemap_lisp = iter.lisp();
+        tilemap_lisp->get("width",  width);
+        tilemap_lisp->get("height", height);
+        tilemap_lisp->get_vector("data", tilemap);
+      } else if(iter.item() == "properties") {
+        const lisp::Lisp* props = iter.lisp();
+        props->get("name", name);
+        props->get("music", music);
+        props->get("intro-filename", intro_filename);
+        props->get("start_pos_x", start_x);
+        props->get("start_pos_y", start_y);
+      } else if(iter.item() == "special-tiles") {
+        parse_special_tiles(iter.lisp());
+      } else {
+        std::cerr << "Unknown token '" << iter.item() << "' in worldmap.\n";
+      }
+    }
 
-          if (strcmp(lisp_symbol(lisp_car(element)), "tilemap") == 0)
-            {
-              LispReader reader(lisp_cdr(element));
-              reader.read_int("width",  width);
-              reader.read_int("height", height);
-              reader.read_int_vector("data", tilemap);
-            }
-          else if (strcmp(lisp_symbol(lisp_car(element)), "properties") == 0)
-            {
-              LispReader reader(lisp_cdr(element));
-              reader.read_string("name", name, true);
-              reader.read_string("music", music);
-             reader.read_int("start_pos_x", start_x);
-             reader.read_int("start_pos_y", start_y);
-            }
-          else if (strcmp(lisp_symbol(lisp_car(element)), "special-tiles") == 0 ||
-                   strcmp(lisp_symbol(lisp_car(element)), "levels") == 0)
-            {
-              lisp_object_t* cur = lisp_cdr(element);
-              
-              while(!lisp_nil_p(cur))
-                {
-                  lisp_object_t* element = lisp_car(cur);
+    delete tux;
+    tux = new Tux(this);
+  } catch(std::exception& e) {
+    std::stringstream msg;
+    msg << "Problem when parsing worldmap '" << map_filename << "': " <<
+      e.what();
+    throw std::runtime_error(msg.str());
+  }
+}
 
-                  if (strcmp(lisp_symbol(lisp_car(element)), "special-tile") == 0)
-                    {
-                      SpecialTile special_tile;
-                      LispReader reader(lisp_cdr(element));
-                      
-                      reader.read_float("x", special_tile.pos.x);
-                      reader.read_float("y", special_tile.pos.y);
-
-                      special_tile.map_message.erase();
-                      reader.read_string("map-message", special_tile.map_message);
-                      special_tile.passive_message = false;
-                      reader.read_bool("passive-message", special_tile.passive_message);
-
-                      special_tile.teleport_dest = Vector(-1,-1);
-                      reader.read_float("teleport-to-x", special_tile.teleport_dest.x);
-                      reader.read_float("teleport-to-y", special_tile.teleport_dest.y);
-
-                      special_tile.invisible = false;
-                      reader.read_bool("invisible-tile", special_tile.invisible);
-
-                      special_tile.apply_action_north = special_tile.apply_action_south =
-                          special_tile.apply_action_east = special_tile.apply_action_west =
-                          true;
-
-                      std::string apply_direction;
-                      reader.read_string("apply-to-direction", apply_direction);
-                      if(!apply_direction.empty())
-                        {
-                        special_tile.apply_action_north = special_tile.apply_action_south =
-                            special_tile.apply_action_east = special_tile.apply_action_west =
-                            false;
-                        if(apply_direction.find("north") != std::string::npos)
-                          special_tile.apply_action_north = true;
-                        if(apply_direction.find("south") != std::string::npos)
-                          special_tile.apply_action_south = true;
-                        if(apply_direction.find("east") != std::string::npos)
-                          special_tile.apply_action_east = true;
-                        if(apply_direction.find("west") != std::string::npos)
-                          special_tile.apply_action_west = true;
-                        }
-
-                      special_tiles.push_back(special_tile);
-                    }
+void
+WorldMap::parse_special_tiles(const lisp::Lisp* lisp)
+{
+  lisp::ListIterator iter(lisp);
+  while(iter.next()) {
+    if(iter.item() == "special-tile") {
+      SpecialTile special_tile;
+
+      const lisp::Lisp* lisp = iter.lisp();
+      lisp->get("x", special_tile.pos.x);
+      lisp->get("y", special_tile.pos.y);
+      lisp->get("map-message", special_tile.map_message);
+      special_tile.passive_message = false;
+      lisp->get("passive-message", special_tile.passive_message);
+      special_tile.teleport_dest = Vector(-1,-1);
+      lisp->get("teleport-to-x", special_tile.teleport_dest.x);
+      lisp->get("teleport-to-y", special_tile.teleport_dest.y);
+      special_tile.invisible = false;
+      lisp->get("invisible-tile", special_tile.invisible);
+
+      special_tile.apply_action_north = true;
+      special_tile.apply_action_south = true;
+      special_tile.apply_action_east = true;
+      special_tile.apply_action_west = true;
+
+      std::string apply_direction;
+      lisp->get("apply-to-direction", apply_direction);
+      if(!apply_direction.empty()) {
+        special_tile.apply_action_north = false;
+        special_tile.apply_action_south = false;
+        special_tile.apply_action_east = false;
+        special_tile.apply_action_west = false;
+        if(apply_direction.find("north") != std::string::npos)
+          special_tile.apply_action_north = true;
+        if(apply_direction.find("south") != std::string::npos)
+          special_tile.apply_action_south = true;
+        if(apply_direction.find("east") != std::string::npos)
+          special_tile.apply_action_east = true;
+        if(apply_direction.find("west") != std::string::npos)
+          special_tile.apply_action_west = true;
+      }
+      
+      special_tiles.push_back(special_tile);
+    } else if(iter.item() == "level") {
+      Level level;
 
-                  else if (strcmp(lisp_symbol(lisp_car(element)), "level") == 0)
-                    {
-                      Level level;
-                      LispReader reader(lisp_cdr(element));
-                      level.solved = false;
+      lisp::Lisp* level_lisp = iter.lisp();
+      level.solved = false;
                       
-                      level.north = true;
-                      level.east  = true;
-                      level.south = true;
-                      level.west  = true;
+      level.north = true;
+      level.east  = true;
+      level.south = true;
+      level.west  = true;
 
-                      reader.read_string("extro-filename", level.extro_filename);
-                      reader.read_string("next-worldmap", level.next_worldmap);
+      level_lisp->get("extro-filename", level.extro_filename);
+      level_lisp->get("next-worldmap", level.next_worldmap);
 
-                      level.quit_worldmap = false;
-                      reader.read_bool("quit-worldmap", level.quit_worldmap);
+      level.quit_worldmap = false;
+      level_lisp->get("quit-worldmap", level.quit_worldmap);
 
-                      reader.read_string("name", level.name, true);
-                      reader.read_float("x", level.pos.x);
-                      reader.read_float("y", level.pos.y);
+      level_lisp->get("name", level.name);
+      level_lisp->get("x", level.pos.x);
+      level_lisp->get("y", level.pos.y);
 
-                      level.auto_path = true;
-                      reader.read_bool("auto-path", level.auto_path);
+      level.auto_path = true;
+      level_lisp->get("auto-path", level.auto_path);
 
-                      level.vertical_flip = false;
-                      reader.read_bool("vertical-flip", level.vertical_flip);
+      level.vertical_flip = false;
+      level_lisp->get("vertical-flip", level.vertical_flip);
 
-                      levels.push_back(level);
-                    }
-                  
-                  cur = lisp_cdr(cur);      
-                }
-            }
-          else
-            {
-              
-            }
-          
-          cur = lisp_cdr(cur);
-        }
+      levels.push_back(level);
+    } else {
+      std::cerr << "Unknown token '" << iter.item() <<
+        "' in worldmap special-tiles list.";
     }
-
-    lisp_free(root_obj);
-
-    delete tux;
-    tux = new Tux(this);
+  }
 }
 
-void WorldMap::get_level_title(Level& level)
+void
+WorldMap::get_level_title(Level& level)
 {
   /** get special_tile's title */
   level.title = "<no title>";
 
-  LispReader* reader = LispReader::load(datadir + "/levels/" + level.name, "supertux-level");
-  if(!reader)
-    {
-    std::cerr << "Error: Could not open level file. Ignoring...\n";
-    return;
-    }
+  try {
+    lisp::Parser parser;
+    std::auto_ptr<lisp::Lisp> root (
+        parser.parse(get_resource_filename(levels_path + level.name)));
 
-  reader->read_string("name", level.title, true);
-  delete reader;
+    const lisp::Lisp* level_lisp = root->get_lisp("supertux-level");
+    if(!level_lisp)
+      return;
+    
+    level_lisp->get("name", level.title);
+  } catch(std::exception& e) {
+    std::cerr << "Problem when reading leveltitle: " << e.what() << "\n";
+    return;
+  }
 }
 
 void WorldMap::calculate_total_stats()
@@ -793,31 +658,25 @@ WorldMap::path_ok(Direction direction, Vector old_pos, Vector* new_pos)
     { // New position is outsite the tilemap
       return false;
     }
-  else if(at(*new_pos)->one_way != BOTH_WAYS)
-    {
-std::cerr << "one way only\n";
-      if((at(*new_pos)->one_way == NORTH_SOUTH_WAY && direction != D_SOUTH) ||
-         (at(*new_pos)->one_way == SOUTH_NORTH_WAY && direction != D_NORTH) ||
-         (at(*new_pos)->one_way == EAST_WEST_WAY && direction != D_WEST) ||
-         (at(*new_pos)->one_way == WEST_EAST_WAY && direction != D_EAST))
-        return false;
-      return true;
-    }
   else
-    { // Check if we the tile allows us to go to new_pos
+    { // Check if the tile allows us to go to new_pos
       switch(direction)
         {
         case D_WEST:
-          return (at(old_pos)->west && at(*new_pos)->east);
+          return (at(old_pos)->getData() & Tile::WORLDMAP_WEST
+              && at(*new_pos)->getData() & Tile::WORLDMAP_EAST);
 
         case D_EAST:
-          return (at(old_pos)->east && at(*new_pos)->west);
+          return (at(old_pos)->getData() & Tile::WORLDMAP_EAST
+              && at(*new_pos)->getData() & Tile::WORLDMAP_WEST);
 
         case D_NORTH:
-          return (at(old_pos)->north && at(*new_pos)->south);
+          return (at(old_pos)->getData() & Tile::WORLDMAP_NORTH
+              && at(*new_pos)->getData() & Tile::WORLDMAP_SOUTH);
 
         case D_SOUTH:
-          return (at(old_pos)->south && at(*new_pos)->north);
+          return (at(old_pos)->getData() & Tile::WORLDMAP_SOUTH
+              && at(*new_pos)->getData() & Tile::WORLDMAP_NORTH);
 
         case D_NONE:
           assert(!"path_ok() can't work if direction is NONE");
@@ -829,12 +688,6 @@ std::cerr << "one way only\n";
 void
 WorldMap::update(float delta)
 {
-  if(!frame_timer.check())
-    {
-    frame_timer.start(25);
-    global_frame_counter++;
-    }
-
   if (enter_level && !tux->is_moving())
     {
       /* Check special tile action */
@@ -857,130 +710,134 @@ WorldMap::update(float delta)
       if (!level)
         {
         std::cout << "No level to enter at: "
-          << tux->get_tile_pos().x << ", " << tux->get_tile_pos().y << std::endl;
+          << tux->get_tile_pos().x << ", " << tux->get_tile_pos().y
+          << std::endl;
         return;
         }
 
 
-          if (level->pos == tux->get_tile_pos())
+      if (level->pos == tux->get_tile_pos())
+        {
+          PlayerStatus old_player_status = player_status;
+
+          std::cout << "Enter the current level: " << level->name << std::endl;
+          // do a shriking fade to the level
+          shrink_fade(Vector((level->pos.x*32 + 16 + offset.x),
+                             (level->pos.y*32 + 16 + offset.y)), 500);
+          GameSession session(get_resource_filename(levels_path + level->name),
+                              ST_GL_LOAD_LEVEL_FILE, &level->statistics);
+
+          switch (session.run())
             {
-              PlayerStatus old_player_status = player_status;
+            case GameSession::ES_LEVEL_FINISHED:
+              {
+                level_finished = true;
+                bool old_level_state = level->solved;
+                level->solved = true;
+
+                // deal with statistics
+                level->statistics.merge(global_stats);
+                calculate_total_stats();
+
+                if (session.get_current_sector()->player->got_power !=
+                      session.get_current_sector()->player->NONE_POWER)
+                  player_status.bonus = PlayerStatus::FLOWER_BONUS;
+                else if (session.get_current_sector()->player->size == BIG)
+                  player_status.bonus = PlayerStatus::GROWUP_BONUS;
+                else
+                  player_status.bonus = PlayerStatus::NO_BONUS;
 
-              std::cout << "Enter the current level: " << level->name << std::endl;
-              // do a shriking fade to the level
-              shrink_fade(Vector((level->pos.x*32 + 16 + offset.x),(level->pos.y*32 + 16
-                      + offset.y)), 500);
-              GameSession session(level->name,
-                                  ST_GL_LOAD_LEVEL_FILE, level->vertical_flip,
-                                  &level->statistics);
+                if (old_level_state != level->solved && level->auto_path)
+                  { // Try to detect the next direction to which we should walk
+                    // FIXME: Mostly a hack
+                    Direction dir = D_NONE;
+                
+                    const Tile* tile = at(tux->get_tile_pos());
 
-              switch (session.run())
-                {
-                case GameSession::ES_LEVEL_FINISHED:
-                  {
-                    level_finished = true;
-                    bool old_level_state = level->solved;
-                    level->solved = true;
-
-                    // deal with statistics
-                    level->statistics.merge(global_stats);
-                    calculate_total_stats();
-
-                    if (session.get_current_sector()->player->got_power !=
-                          session.get_current_sector()->player->NONE_POWER)
-                      player_status.bonus = PlayerStatus::FLOWER_BONUS;
-                    else if (session.get_current_sector()->player->size == BIG)
-                      player_status.bonus = PlayerStatus::GROWUP_BONUS;
-                    else
-                      player_status.bonus = PlayerStatus::NO_BONUS;
-
-                    if (old_level_state != level->solved && level->auto_path)
-                      { // Try to detect the next direction to which we should walk
-                        // FIXME: Mostly a hack
-                        Direction dir = D_NONE;
-                    
-                        Tile* tile = at(tux->get_tile_pos());
-
-                        if (tile->north && tux->back_direction != D_NORTH)
-                          dir = D_NORTH;
-                        else if (tile->south && tux->back_direction != D_SOUTH)
-                          dir = D_SOUTH;
-                        else if (tile->east && tux->back_direction != D_EAST)
-                          dir = D_EAST;
-                        else if (tile->west && tux->back_direction != D_WEST)
-                          dir = D_WEST;
-
-                        if (dir != D_NONE)
-                          {
-                            tux->set_direction(dir);
-                            //tux->update(delta);
-                          }
-
-                        std::cout << "Walk to dir: " << dir << std::endl;
+                    if (tile->getData() & Tile::WORLDMAP_NORTH
+                        && tux->back_direction != D_NORTH)
+                      dir = D_NORTH;
+                    else if (tile->getData() & Tile::WORLDMAP_SOUTH
+                        && tux->back_direction != D_SOUTH)
+                      dir = D_SOUTH;
+                    else if (tile->getData() & Tile::WORLDMAP_EAST
+                        && tux->back_direction != D_EAST)
+                      dir = D_EAST;
+                    else if (tile->getData() & Tile::WORLDMAP_WEST
+                        && tux->back_direction != D_WEST)
+                      dir = D_WEST;
+
+                    if (dir != D_NONE)
+                      {
+                        tux->set_direction(dir);
+                        //tux->update(delta);
                       }
+
+                    std::cout << "Walk to dir: " << dir << std::endl;
                   }
+              }
 
-                  break;
-                case GameSession::ES_LEVEL_ABORT:
-                  level_finished = false;
-                  /* In case the player's abort the level, keep it using the old
-                      status. But the minimum lives and no bonus. */
-                  player_status.distros = old_player_status.distros;
-                  player_status.lives = std::min(old_player_status.lives, player_status.lives);
-                  player_status.bonus = player_status.NO_BONUS;
-
-                  break;
-                case GameSession::ES_GAME_OVER:
-                  {
-                  level_finished = false;
-                  /* draw an end screen */
-                  /* TODO: in the future, this should make a dialog a la SuperMario, asking
-                  if the player wants to restart the world map with no score and from
-                  level 1 */
-                  char str[80];
+              break;
+            case GameSession::ES_LEVEL_ABORT:
+              level_finished = false;
+              /* In case the player's abort the level, keep it using the old
+                  status. But the minimum lives and no bonus. */
+              player_status.distros = old_player_status.distros;
+              player_status.lives = std::min(old_player_status.lives, player_status.lives);
+              player_status.bonus = player_status.NO_BONUS;
 
-                  DrawingContext context;
-                  context.draw_gradient(Color (200,240,220), Color(200,200,220),
-                      LAYER_BACKGROUND0);
+              break;
+            case GameSession::ES_GAME_OVER:
+              {
+              level_finished = false;
+              /* draw an end screen */
+              /* TODO: in the future, this should make a dialog a la SuperMario, asking
+              if the player wants to restart the world map with no score and from
+              level 1 */
+              char str[80];
 
-                  context.draw_text(blue_text, _("GAMEOVER"), 
-                      Vector(screen->w/2, 200), CENTER_ALLIGN, LAYER_FOREGROUND1);
+              DrawingContext context;
+              context.draw_gradient(Color (200,240,220), Color(200,200,220),
+                  LAYER_BACKGROUND0);
 
-                  sprintf(str, _("COINS: %d"), player_status.distros);
-                  context.draw_text(gold_text, str,
-                      Vector(screen->w/2, screen->w - 32), CENTER_ALLIGN, LAYER_FOREGROUND1);
+              context.draw_text(blue_text, _("GAMEOVER"), 
+                  Vector(screen->w/2, 200), CENTER_ALLIGN, LAYER_FOREGROUND1);
 
-                  total_stats.draw_message_info(context, _("Total Statistics"));
+              sprintf(str, _("COINS: %d"), player_status.distros);
+              context.draw_text(gold_text, str,
+                  Vector(screen->w/2, screen->w - 32), CENTER_ALLIGN,
+                  LAYER_FOREGROUND1);
 
-                  context.do_drawing();
-  
-                  SDL_Event event;
-                  wait_for_event(event,2000,6000,true);
+              total_stats.draw_message_info(context, _("Total Statistics"));
 
-                  quit = true;
-                  player_status.reset();
-                  break;
-                  }
-                case GameSession::ES_NONE:
-                  assert(false);
-                  // Should never be reached 
-                  break;
-                }
+              context.do_drawing();
+
+              SDL_Event event;
+              wait_for_event(event,2000,6000,true);
 
-              SoundManager::get()->play_music(song);
-              Menu::set_current(0);
-              if (!savegame_file.empty())
-                savegame(savegame_file);
+              quit = true;
+              player_status.reset();
+              break;
+              }
+            case GameSession::ES_NONE:
+              assert(false);
+              // Should never be reached 
+              break;
             }
+
+          SoundManager::get()->play_music(song);
+          Menu::set_current(0);
+          if (!savegame_file.empty())
+            savegame(savegame_file);
+        }
       /* The porpose of the next checking is that if the player lost
          the level (in case there is one), don't show anything */
-      if(level_finished)
-        {
-        if (!level->extro_filename.empty())
-          {
+      if(level_finished) {
+        if (!level->extro_filename.empty()) {
           // Display a text file
-          display_text_file(level->extro_filename, SCROLL_SPEED_MESSAGE, white_big_text , white_text, white_small_text, blue_text );
-          }
+          std::string filename = levels_path + level->extro_filename;
+          display_text_file(filename);
+        }
 
         if (!level->next_worldmap.empty())
           {
@@ -1020,7 +877,7 @@ WorldMap::update(float delta)
     }
 }
 
-Tile*
+const Tile*
 WorldMap::at(Vector p)
 {
   assert(p.x >= 0 
@@ -1064,8 +921,9 @@ WorldMap::draw(DrawingContext& context, const Vector& offset)
   for(int y = 0; y < height; ++y)
     for(int x = 0; x < width; ++x)
       {
-        Tile* tile = at(Vector(x, y));
-        tile->draw(context, Vector(x*32 + offset.x, y*32 + offset.y));
+        const Tile* tile = at(Vector(x, y));
+        tile->draw(context, Vector(x*32 + offset.x, y*32 + offset.y),
+            LAYER_TILES);
       }
 
   for(Levels::iterator i = levels.begin(); i != levels.end(); ++i)
@@ -1180,50 +1038,46 @@ WorldMap::display()
   song = SoundManager::get()->load_music(datadir +  "/music/" + music);
   SoundManager::get()->play_music(song);
 
-  FrameRate frame_rate(10);
-  frame_rate.set_frame_limit(false);
-
-  frame_rate.start();
+  if(!intro_displayed && intro_filename != "") {
+    std::string filename = levels_path + intro_filename;
+    display_text_file(filename);
+    intro_displayed = true;
+  }
 
+  Uint32 lastticks = SDL_GetTicks();
   DrawingContext context;
-  while(!quit)
-    {
-      float delta = frame_rate.get();
-
-      delta *= 1.3f;
-
-      if (delta > 10.0f)
-        delta = .3f;
-       
-      frame_rate.update();
-
-      Vector tux_pos = tux->get_pos();
-      if (1)
-        {
-          offset.x = -tux_pos.x + screen->w/2;
-          offset.y = -tux_pos.y + screen->h/2;
-
-          if (offset.x > 0) offset.x = 0;
-          if (offset.y > 0) offset.y = 0;
-
-          if (offset.x < screen->w - width*32) offset.x = screen->w - width*32;
-          if (offset.y < screen->h - height*32) offset.y = screen->h - height*32;
-        } 
-
-      draw(context, offset);
-      get_input();
-      update(delta);
+  while(!quit) {
+    Uint32 ticks = SDL_GetTicks();
+    float elapsed_time = float(ticks - lastticks) / 1000;
+    global_time += elapsed_time;
+    lastticks = ticks;
+    
+    // 40 fps minimum
+    if(elapsed_time > .025)
+      elapsed_time = .025;
+    
+    Vector tux_pos = tux->get_pos();
+    
+    offset.x = -tux_pos.x + screen->w/2;
+    offset.y = -tux_pos.y + screen->h/2;
+
+    if (offset.x > 0) offset.x = 0;
+    if (offset.y > 0) offset.y = 0;
+
+    if (offset.x < screen->w - width*32) offset.x = screen->w - width*32;
+    if (offset.y < screen->h - height*32) offset.y = screen->h - height*32;
+    
+    draw(context, offset);
+    get_input();
+    update(elapsed_time);
       
-      if(Menu::current())
-        {
-          Menu::current()->draw(context);
-          mouse_cursor->draw(context);
-        }
-
-      context.do_drawing();
-
-      SDL_Delay(20);
+    if(Menu::current()) {
+      Menu::current()->draw(context);
+      mouse_cursor->draw(context);
     }
+
+    context.do_drawing();
+  }
 }
 
 void
@@ -1232,61 +1086,58 @@ WorldMap::savegame(const std::string& filename)
   if(filename == "")
     return;
 
-  std::cout << "savegame: " << filename << std::endl;
-
-   std::ofstream file(filename.c_str(), std::ios::out);
-   LispWriter* writer = new LispWriter(file);
+  std::ofstream file(filename.c_str(), std::ios::out);
+  lisp::Writer writer(file);
 
   int nb_solved_levels = 0, total_levels = 0;
-  for(Levels::iterator i = levels.begin(); i != levels.end(); ++i)
-    {
-      ++total_levels;
-      if (i->solved)
-        ++nb_solved_levels;
-    }
+  for(Levels::iterator i = levels.begin(); i != levels.end(); ++i) {
+    ++total_levels;
+    if (i->solved)
+      ++nb_solved_levels;
+  }
   char nb_solved_levels_str[80], total_levels_str[80];
   sprintf(nb_solved_levels_str, "%d", nb_solved_levels);
   sprintf(total_levels_str, "%d", total_levels);
 
-  writer->write_comment("Worldmap save file");
+  writer.write_comment("Worldmap save file");
 
-  writer->start_list("supertux-savegame");
+  writer.start_list("supertux-savegame");
 
-  writer->write_int("version", 1);
-  writer->write_string("title", std::string(name + " - " + nb_solved_levels_str + "/" + total_levels_str));
-  writer->write_string("map", map_filename);
-  writer->write_int("lives", player_status.lives);
-  writer->write_int("distros", player_status.lives);
-  writer->write_int("max-score-multiplier", player_status.max_score_multiplier);
+  writer.write_int("version", 1);
+  writer.write_string("title",
+      std::string(name + " - " + nb_solved_levels_str+"/"+total_levels_str));
+  writer.write_string("map", map_filename);
+  writer.write_bool("intro-displayed", intro_displayed);
 
-  writer->start_list("tux");
+  writer.start_list("tux");
 
-  writer->write_float("x", tux->get_tile_pos().x);
-  writer->write_float("y", tux->get_tile_pos().y);
-  writer->write_string("back", direction_to_string(tux->back_direction));
-  writer->write_string("bonus", bonus_to_string(player_status.bonus));
+  writer.write_float("x", tux->get_tile_pos().x);
+  writer.write_float("y", tux->get_tile_pos().y);
+  writer.write_string("back", direction_to_string(tux->back_direction));
+  player_status.write(writer);
+  writer.write_string("back", direction_to_string(tux->back_direction));
 
-  writer->end_list("tux");
+  writer.end_list("tux");
 
-  writer->start_list("levels");
+  writer.start_list("levels");
 
   for(Levels::iterator i = levels.begin(); i != levels.end(); ++i)
     {
       if (i->solved)
         {
-        writer->start_list("level");
+        writer.start_list("level");
 
-        writer->write_string("name", i->name);
-        writer->write_bool("solved", true);
-        i->statistics.write(*writer);
+        writer.write_string("name", i->name);
+        writer.write_bool("solved", true);
+        i->statistics.write(writer);
 
-        writer->end_list("level");
+        writer.end_list("level");
         }
     }  
 
-  writer->end_list("levels");
+  writer.end_list("levels");
 
-  writer->end_list("supertux-savegame");
+  writer.end_list("supertux-savegame");
 }
 
 void
@@ -1295,98 +1146,74 @@ WorldMap::loadgame(const std::string& filename)
   std::cout << "loadgame: " << filename << std::endl;
   savegame_file = filename;
 
-  if (access(filename.c_str(), F_OK) != 0)
-    {
-    load_map();
-
-    player_status.reset();
-
-    return;
-    }
+  try {
+    lisp::Parser parser;
+    std::auto_ptr<lisp::Lisp> root (parser.parse(filename));
   
-  lisp_object_t* savegame = lisp_read_from_file(filename);
-  if (!savegame)
-    {
-      std::cout << "WorldMap:loadgame: File not found: " << filename << std::endl;
-      load_map();
-      return;
-    }
+    const lisp::Lisp* savegame = root->get_lisp("supertux-savegame");
+    if(!savegame)
+      throw std::runtime_error("File is not a supertux-savegame file.");
 
-  lisp_object_t* cur = savegame;
-
-  if (strcmp(lisp_symbol(lisp_car(cur)), "supertux-savegame") != 0)
-    {
-    load_map();
-    return;
-    }
-
-  cur = lisp_cdr(cur);
-  LispReader reader(cur);
-
-  /* Get the Map filename and then load it before setting level settings */
-  std::string cur_map_filename = map_filename;
-  reader.read_string("map", map_filename);
-//  if(cur_map_filename != map_filename)
+    /* Get the Map filename and then load it before setting level settings */
+    std::string cur_map_filename = map_filename;
+    savegame->get("map", map_filename);
     load_map(); 
 
-  reader.read_int("lives", player_status.lives);
-  reader.read_int("distros", player_status.distros);
-  reader.read_int("max-score-multiplier", player_status.max_score_multiplier);
-
-  if (player_status.lives < 0)
-    player_status.lives = START_LIVES;
+    savegame->get("intro-displayed", intro_displayed);
+    savegame->get("lives", player_status.lives);
+    savegame->get("distros", player_status.distros);
+    savegame->get("max-score-multiplier", player_status.max_score_multiplier);
+    if (player_status.lives < 0)
+      player_status.reset();
 
-  lisp_object_t* tux_cur = 0;
-  if (reader.read_lisp("tux", tux_cur))
+    const lisp::Lisp* tux_lisp = savegame->get_lisp("tux");
+    if(tux)
     {
       Vector p;
       std::string back_str = "none";
-      std::string bonus_str = "none";
 
-      LispReader tux_reader(tux_cur);
-      tux_reader.read_float("x", p.x);
-      tux_reader.read_float("y", p.y);
-      tux_reader.read_string("back", back_str);
-      tux_reader.read_string("bonus", bonus_str);
+      tux_lisp->get("x", p.x);
+      tux_lisp->get("y", p.y);
+      tux_lisp->get("back", back_str);
+      player_status.read(*tux_lisp);
       
-      player_status.bonus = string_to_bonus(bonus_str);
       tux->back_direction = string_to_direction(back_str);      
       tux->set_tile_pos(p);
     }
 
-  lisp_object_t* level_cur = 0;
-  if (reader.read_lisp("levels", level_cur))
-    {
-      while(level_cur)
-        {
-          lisp_object_t* sym  = lisp_car(lisp_car(level_cur));
-          lisp_object_t* data = lisp_cdr(lisp_car(level_cur));
+    const lisp::Lisp* levels_lisp = savegame->get_lisp("levels");
+    if(levels_lisp) {
+      lisp::ListIterator iter(levels_lisp);
+      while(iter.next()) {
+        if(iter.item() == "level") {
+          std::string name;
+          bool solved = false;
 
-          if (strcmp(lisp_symbol(sym), "level") == 0)
-            {
-              std::string name;
-              bool solved = false;
+          const lisp::Lisp* level = iter.lisp();
+          level->get("name", name);
+          level->get("solved", solved);
 
-              LispReader level_reader(data);
-              level_reader.read_string("name", name);
-              level_reader.read_bool("solved", solved);
-
-              for(Levels::iterator i = levels.begin(); i != levels.end(); ++i)
-                {
-                  if (name == i->name)
-                    {
-                    i->solved = solved;
-                    i->statistics.parse(level_reader);
-                    break;
-                    }
-                }
+          for(Levels::iterator i = levels.begin(); i != levels.end(); ++i)
+          {
+            if (name == i->name)
+            {
+              i->solved = solved;
+              i->statistics.parse(*level);
+              break;
             }
-
-          level_cur = lisp_cdr(level_cur);
+          }
+        } else {
+          std::cerr << "Unknown token '" << iter.item() 
+            << "' in levels block in worldmap.\n";
         }
+      }
     }
-  lisp_free(savegame);
+  } catch(std::exception& e) {
+    std::cerr << "Problem loading game '" << filename << "': " << e.what() 
+      << "\n";
+    load_map();
+    player_status.reset();
+  }
 
   calculate_total_stats();
 }
@@ -1400,8 +1227,3 @@ WorldMap::loadmap(const std::string& filename)
 }
 
 } // namespace WorldMapNS
-
-/* Local Variables: */
-/* mode:c++ */
-/* End: */
-