- added zlib support to lispreader
[supertux.git] / src / worldmap.cpp
index 6d98118..9cd8ba5 100644 (file)
 //  along with this program; if not, write to the Free Software
 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
+#include <iostream>
 #include <vector>
 #include <assert.h>
 #include "texture.h"
 #include "screen.h"
 #include "lispreader.h"
+#include "gameloop.h"
 #include "worldmap.h"
 
 namespace WorldMapNS {
@@ -30,12 +32,10 @@ TileManager* TileManager::instance_  = 0;
 
 TileManager::TileManager()
 {
-  lisp_stream_t stream;
-  FILE* in = fopen(DATA_PREFIX "images/worldmap/antarctica.scm", "r");
-  assert(in);
-  lisp_stream_init_file (&stream, in);
-  lisp_object_t* root_obj = lisp_read (&stream);
+  lisp_object_t* root_obj = lisp_read_from_file(DATA_PREFIX "images/worldmap/antarctica.scm");
   
+  assert(root_obj);
+
   if (strcmp(lisp_symbol(lisp_car(root_obj)), "supertux-worldmap-tiles") == 0)
     {
       lisp_object_t* cur = lisp_cdr(root_obj);
@@ -69,7 +69,7 @@ TileManager::TileManager()
                            const_cast<char*>((std::string(DATA_PREFIX "/images/worldmap/") + filename).c_str()), 
                            USE_ALPHA);
 
-              if (id >= tiles.size())
+              if (id >= int(tiles.size()))
                 tiles.resize(id+1);
 
               tiles[id] = tile;
@@ -91,49 +91,184 @@ TileManager::TileManager()
 Tile*
 TileManager::get(int i)
 {
-  assert(i >=0 && i < tiles.size());
+  assert(i >=0 && i < int(tiles.size()));
   return tiles[i];
 }
 
+Tux::Tux(WorldMap* worldmap_)
+  : worldmap(worldmap_)
+{
+  texture_load(&sprite, DATA_PREFIX "/images/worldmap/tux.png", USE_ALPHA);
+  offset = 0;
+  moving = false;
+  tile_pos.x = 0;
+  tile_pos.y = 0;
+  direction = NONE;
+  input_direction = NONE;
+}
+
+void
+Tux::draw()
+{
+  float x = tile_pos.x * 32;
+  float y = tile_pos.y * 32;
+
+  switch(direction)
+    {
+    case WEST:
+      x -= offset - 32;
+      break;
+    case EAST:
+      x += offset - 32;
+      break;
+    case NORTH:
+      y -= offset - 32;
+      break;
+    case SOUTH:
+      y += offset - 32;
+      break;
+    case NONE:
+      break;
+    }
+
+  texture_draw(&sprite, (int)x, (int)y, NO_UPDATE);
+}
+
+void
+Tux::stop()
+{
+  offset = 0;
+  direction = NONE;
+  moving = false;
+}
+
+void
+Tux::update(float delta)
+{
+  if (!moving)
+    {
+      if (input_direction != NONE)
+        { // We got a new direction, so lets start walking when possible
+          Point next_tile;
+          if (worldmap->path_ok(input_direction, tile_pos, &next_tile))
+            {
+              tile_pos = next_tile;
+              moving = true;
+              direction = input_direction;
+            }
+        }
+    }
+  else
+    {
+      // Let tux walk a few pixels (20 pixel/sec)
+      offset += 20.0f * delta;
+
+      if (offset > 32)
+        { // We reached the next tile, so we check what to do now
+          offset -= 32;
+
+          if (worldmap->at(tile_pos)->stop)
+            {
+              stop();
+            }
+          else
+            {
+              Point next_tile;
+              if (worldmap->path_ok(direction, tile_pos, &next_tile))
+                {
+                  tile_pos = next_tile;
+                }
+              else
+                {
+                  puts("Tilemap data is buggy");
+                  stop();
+                }
+            }
+        }
+    }
+}
+
 WorldMap::WorldMap()
 {
+  tux = new Tux(this);
+
   quit = false;
   width  = 20;
   height = 15;
-  tux_moving = false;
-
-  texture_load(&tux_sprite, DATA_PREFIX "/images/worldmap/tux.png", USE_ALPHA);
 
-  tux_offset = 0;
-
-  tux_tile_pos.x = 0;
-  tux_tile_pos.y = 0;
+  texture_load(&level_sprite, DATA_PREFIX "/images/worldmap/levelmarker.png", USE_ALPHA);
 
   input_direction = NONE;
-  tux_direction = NONE;
   enter_level = false;
 
+  name = "<no name>";
+  music = "SALCON.MOD";
+  song = 0;
+
   load_map();
 }
 
 WorldMap::~WorldMap()
 {
+  delete tux;
 }
 
 void
 WorldMap::load_map()
 {
-  tilemap.resize(width * height);
+  lisp_object_t* root_obj = lisp_read_from_file(DATA_PREFIX "levels/default/worldmap.scm");
+  assert(root_obj);
   
-  tilemap[0] = 5;
-  tilemap[1] = 1;
-  tilemap[2] = 6;
-  tilemap[3] = 1;
-  tilemap[4] = 3;
-  tilemap[4+20] = 2;
-  tilemap[4+40] = 7;
-  tilemap[4+60] = 2;
-  tilemap[4+80] = 4;
+  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);
+
+          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);
+              reader.read_string("music", &music);
+            }
+          else if (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);
+                  
+                  if (strcmp(lisp_symbol(lisp_car(element)), "level") == 0)
+                    {
+                      Level level;
+                      LispReader reader(lisp_cdr(element));
+                      reader.read_string("name",  &level.name);
+                      reader.read_int("x-pos", &level.x);
+                      reader.read_int("y-pos", &level.y);
+                      levels.push_back(level);
+                    }
+                  
+                  cur = lisp_cdr(cur);      
+                }
+            }
+          else
+            {
+              
+            }
+          
+          cur = lisp_cdr(cur);
+        }
+    }
 }
 
 void
@@ -159,8 +294,9 @@ WorldMap::get_input()
               break;
             case SDLK_LCTRL:
             case SDLK_RETURN:
-              if (!tux_moving)
-                enter_level = true;
+              enter_level = true;
+              break;
+            default:
               break;
             }
           break;
@@ -181,100 +317,86 @@ WorldMap::get_input()
     input_direction = SOUTH;
 }
 
-void
-WorldMap::update()
+Point
+WorldMap::get_next_tile(Point pos, Direction direction)
 {
-  float speed = 4.5;
-
-  if (enter_level)
+  switch(direction)
     {
-      puts("Enter the current level");
+    case WEST:
+      pos.x -= 1;
+      break;
+    case EAST:
+      pos.x += 1;
+      break;
+    case NORTH:
+      pos.y -= 1;
+      break;
+    case SOUTH:
+      pos.y += 1;
+      break;
+    case NONE:
+      break;
+    }
+  return pos;
+}
+
+bool
+WorldMap::path_ok(Direction direction, Point old_pos, Point* new_pos)
+{
+  *new_pos = get_next_tile(old_pos, direction);
+
+  if (!(new_pos->x >= 0 && new_pos->x < width
+        && new_pos->y >= 0 && new_pos->y < height))
+    { // New position is outsite the tilemap
+      return false;
     }
   else
-    {
-      if (!tux_moving)
+    { // Check if we the tile allows us to go to new_pos
+      switch(direction)
         {
-          // FIXME: Cleanup, seperate tux
-          switch(input_direction)
-            {
-            case WEST:
-              if (at(tux_tile_pos)->west)
-                {
-                  tux_tile_pos.x -= 1;
-                  tux_moving = true;
-                  tux_direction = input_direction;
-                }
-              break;
-            case EAST:
-              if (at(tux_tile_pos)->east)
-                {
-                  tux_tile_pos.x += 1;
-                  tux_moving = true;
-                  tux_direction = input_direction;
-                }
-              break;
-            case NORTH:
-              if (at(tux_tile_pos)->north)
-                {
-                  tux_tile_pos.y -= 1;
-                  tux_moving = true;
-                  tux_direction = input_direction;
-                }
-              break;
-            case SOUTH:
-              if (at(tux_tile_pos)->south)
-                {
-                  tux_tile_pos.y += 1;
-                  tux_moving = true;
-                  tux_direction = input_direction;
-                }
-              break;
-            case NONE:
-              tux_moving = false;
-              tux_offset = 0;
-              tux_direction = input_direction;
-              break;
-            }
+        case WEST:
+          return (at(old_pos)->west && at(*new_pos)->east);
+
+        case EAST:
+          return (at(old_pos)->east && at(*new_pos)->west);
+
+        case NORTH:
+          return (at(old_pos)->north && at(*new_pos)->south);
+
+        case SOUTH:
+          return (at(old_pos)->south && at(*new_pos)->north);
+
+        case NONE:
+          assert(!"path_ok() can't work if direction is NONE");
         }
-      else
-        {
-          tux_offset += speed;
+      return false;
+    }
+}
 
-          if (tux_offset > 32)
+void
+WorldMap::update()
+{
+  if (enter_level && !tux->is_moving())
+    {
+      for(Levels::iterator i = levels.begin(); i != levels.end(); ++i)
+        {
+          if (i->x == tux->get_tile_pos().x && 
+              i->y == tux->get_tile_pos().y)
             {
-              tux_offset -= 32;
-
-              if (at(tux_tile_pos)->stop)
-                {
-                  tux_direction = NONE;
-                  tux_moving = false;
-                }
-              else
-                {
-                  // FIXME: Cleanup, seperate tux
-                  switch(tux_direction)
-                    {
-                    case WEST:
-                      if (at(tux_tile_pos)->west)
-                        tux_tile_pos.x -= 1;
-                      break;
-                    case EAST:
-                      if (at(tux_tile_pos)->east)
-                        tux_tile_pos.x += 1;
-                      break;
-                    case NORTH:
-                      if (at(tux_tile_pos)->north)
-                        tux_tile_pos.y -= 1;
-                      break;
-                    case SOUTH:
-                      if (at(tux_tile_pos)->south)
-                        tux_tile_pos.y += 1;
-                      break;
-                    }                      
-                }
+              std::cout << "Enter the current level: " << i->name << std::endl;;
+              halt_music();
+              gameloop(const_cast<char*>((DATA_PREFIX "levels/default/" + i->name).c_str()),
+                       1, ST_GL_LOAD_LEVEL_FILE);
+              play_music(song, 1);
+              break;
             }
         }
     }
+  else
+    {
+      tux->set_direction(input_direction);
+      tux->update(0.33f);
+    }
 }
 
 Tile*
@@ -296,28 +418,13 @@ WorldMap::draw()
         Tile* tile = at(Point(x, y));
         texture_draw(&tile->sprite, x*32, y*32, NO_UPDATE);
       }
-
   
-  float x = tux_tile_pos.x * 32;
-  float y = tux_tile_pos.y * 32;
-
-  switch(tux_direction)
+  for(Levels::iterator i = levels.begin(); i != levels.end(); ++i)
     {
-    case WEST:
-      x -= tux_offset - 32;
-      break;
-    case EAST:
-      x += tux_offset - 32;
-      break;
-    case NORTH:
-      y -= tux_offset - 32;
-      break;
-    case SOUTH:
-      y += tux_offset - 32;
-      break;
+      texture_draw(&level_sprite, i->x*32, i->y*32, NO_UPDATE);
     }
 
-  texture_draw(&tux_sprite, (int)x, (int)y, NO_UPDATE);
+  tux->draw();
   flipscreen();
 }
 
@@ -326,12 +433,17 @@ WorldMap::display()
 {
   quit = false;
 
+  song = load_song(const_cast<char*>((DATA_PREFIX "/music/" + music).c_str()));
+  play_music(song, 1);
+
   while(!quit) {
     draw();
     get_input();
     update();
     SDL_Delay(20);
   }
+
+  free_music(song);
 }
 
 } // namespace WorldMapNS