a first implementation of doors to switch between sectors
[supertux.git] / src / gameloop.cpp
index a24f457..08df481 100644 (file)
@@ -1,15 +1,25 @@
-/*
-  gameloop.c
-  
-  Super Tux - Game Loop!
-  
-  by Bill Kendrick & Tobias Glaesser
-  bill@newbreedsoftware.com
-  http://www.newbreedsoftware.com/supertux/
-  
-  April 11, 2000 - March 15, 2004
-*/
-
+//  $Id$
+// 
+//  SuperTux
+//  Copyright (C) 2000 Bill Kendrick <bill@newbreedsoftware.com>
+//  Copyright (C) 2004 Tobias Glaesser <tobi.web@gmx.de>
+//  Copyright (C) 2004 Ingo Ruhnke <grumbel@gmx.de>
+//
+//  This program is free software; you can redistribute it and/or
+//  modify it under the terms of the GNU General Public License
+//  as published by the Free Software Foundation; either version 2
+//  of the License, or (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+// 
+//  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 <iostream>
 #include <assert.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include "defines.h"
 #include "globals.h"
 #include "gameloop.h"
-#include "screen.h"
+#include "screen/screen.h"
 #include "setup.h"
 #include "high_scores.h"
 #include "menu.h"
 #include "badguy.h"
-#include "world.h"
+#include "sector.h"
 #include "special.h"
 #include "player.h"
 #include "level.h"
 #include "tile.h"
 #include "particlesystem.h"
 #include "resources.h"
+#include "background.h"
+#include "tilemap.h"
+#include "music_manager.h"
 
 GameSession* GameSession::current_ = 0;
 
-void
-GameSession::init()
-{
-  game_pause = false;
-}
-
-GameSession::GameSession()
+GameSession::GameSession(const std::string& levelname_, int mode)
+  : level(0), currentsector(0), st_gl_mode(mode),
+    end_sequence(NO_ENDSEQUENCE), levelname(levelname_)
 {
   current_ = this;
-  assert(0);
-}
-
-GameSession::GameSession(const std::string& filename)
-{
-  init();
-
-  //assert(!"Don't call me");
-  current_ = this;
+  
+  global_frame_counter = 0;
+  game_pause = false;
+  fps_fps = 0;
 
-  world = new World; // &::global_world;
+  fps_timer.init(true);            
+  frame_timer.init(true);
 
-  timer_init(&fps_timer, true);
-  timer_init(&frame_timer, true);
+  context = new DrawingContext();
 
-  world->load(filename);
+  restart_level();
 }
 
-GameSession::GameSession(const std::string& subset_, int levelnb, int mode)
-  : subset(subset_)
+void
+GameSession::restart_level()
 {
-  init();
-
-  current_ = this;
-
-  world = new World; // &::global_world;
+  game_pause   = false;
+  exit_status  = ES_NONE;
+  end_sequence = NO_ENDSEQUENCE;
 
-  timer_init(&fps_timer, true);
-  timer_init(&frame_timer, true);
+  fps_timer.init(true);
+  frame_timer.init(true);
 
-  st_gl_mode = mode;
-  level      = levelnb;
-
-  /* Init the game: */
-  world->arrays_free();
-  world->set_defaults();
-
-  if (st_gl_mode == ST_GL_LOAD_LEVEL_FILE)
+#if 0
+  float old_x_pos = -1;
+  if (world)
+    { // Tux has lost a life, so we try to respawn him at the nearest reset point
+      old_x_pos = world->get_tux()->base.x;
+    }
+#endif
+  
+  delete level;
+  currentsector = 0;
+
+  level = new Level;
+  level->load(levelname);
+  currentsector = level->get_sector("main");
+  if(!currentsector)
+    st_abort("Level has no main sector.", "");
+  currentsector->activate("main");
+
+#if 0 // TODO
+  // Set Tux to the nearest reset point
+  if (old_x_pos != -1)
     {
-      if (world->load(subset))
-        exit(1);
+      ResetPoint best_reset_point = { -1, -1 };
+      for(std::vector<ResetPoint>::iterator i = get_level()->reset_points.begin();
+          i != get_level()->reset_points.end(); ++i)
+        {
+          if (i->x < old_x_pos && best_reset_point.x < i->x)
+            best_reset_point = *i;
+        }
+      
+      if (best_reset_point.x != -1)
+        {
+          world->get_tux()->base.x = best_reset_point.x;
+          world->get_tux()->base.y = best_reset_point.y;
+        }
     }
-  else
+#endif
+    
+  if (st_gl_mode != ST_GL_DEMO_GAME)
     {
-      if(world->load(subset, level) != 0)
-        exit(1);
+      if(st_gl_mode == ST_GL_PLAY || st_gl_mode == ST_GL_LOAD_LEVEL_FILE)
+        levelintro();
     }
 
-  world->get_level()->load_gfx();
-  loadshared();
-  
-  world->activate_bad_guys();
-  world->activate_particle_systems();
-  world->get_level()->load_song();
-
-  if(st_gl_mode != ST_GL_TEST)
-    load_hs();
-
-  if(st_gl_mode == ST_GL_PLAY || st_gl_mode == ST_GL_LOAD_LEVEL_FILE)
-    levelintro();
-
-  timer_init(&time_left,true);
+  time_left.init(true);
   start_timers();
-
-  if(st_gl_mode == ST_GL_LOAD_GAME)
-    loadgame(levelnb);
+  currentsector->play_music(LEVEL_MUSIC);
 }
 
 GameSession::~GameSession()
 {
-  delete world;
+  delete level;
+  delete context;
 }
 
 void
 GameSession::levelintro(void)
 {
-  Player& tux = *world->get_tux();
-
+  music_manager->halt_music();
+  
   char str[60];
-  /* Level Intro: */
-  clearscreen(0, 0, 0);
 
-  sprintf(str, "LEVEL %d", level);
-  text_drawf(&blue_text, str, 0, 200, A_HMIDDLE, A_TOP, 1);
+  DrawingContext context;
+  currentsector->background->draw(context);
 
-  sprintf(str, "%s", world->get_level()->name.c_str());
-  text_drawf(&gold_text, str, 0, 224, A_HMIDDLE, A_TOP, 1);
+  context.draw_text_center(gold_text, level->get_name(), Vector(0, 220),
+      LAYER_FOREGROUND1);
 
-  sprintf(str, "TUX x %d", tux.lives);
-  text_drawf(&white_text, str, 0, 256, A_HMIDDLE, A_TOP, 1);
+  sprintf(str, "TUX x %d", player_status.lives);
+  context.draw_text_center(white_text, str, Vector(0, 240),
+      LAYER_FOREGROUND1);
+  
+  context.draw_text_center(white_small_text,
+      std::string("by ") + level->get_author(), 
+      Vector(0, 400), LAYER_FOREGROUND1);
 
-  flipscreen();
+  context.do_drawing();
 
   SDL_Event event;
   wait_for_event(event,1000,3000,true);
@@ -156,334 +173,407 @@ GameSession::levelintro(void)
 void
 GameSession::start_timers()
 {
-  timer_start(&time_left, world->get_level()->time_left*1000);
+  time_left.start(level->time_left*1000);
   st_pause_ticks_init();
   update_time = st_get_ticks();
 }
 
 void
-GameSession::process_events()
+GameSession::on_escape_press()
 {
-  Player& tux = *world->get_tux();
+  if(currentsector->player->dying || end_sequence != NO_ENDSEQUENCE)
+    return;   // don't let the player open the menu, when he is dying
+  
+  if(game_pause)
+    return;
 
-  SDL_Event event;
-  while (SDL_PollEvent(&event))
+  if(st_gl_mode == ST_GL_TEST)
+    {
+      exit_status = ES_LEVEL_ABORT;
+    }
+  else if (!Menu::current())
     {
-      /* Check for menu-events, if the menu is shown */
-      if(show_menu)
-        menu_event(event);
+      /* Tell Tux that the keys are all down, otherwise
+        it could have nasty bugs, like going allways to the right
+        or whatever that key does */
+      Player& tux = *(currentsector->player);
+      tux.key_event((SDLKey)keymap.jump, UP);
+      tux.key_event((SDLKey)keymap.duck, UP);
+      tux.key_event((SDLKey)keymap.left, UP);
+      tux.key_event((SDLKey)keymap.right, UP);
+      tux.key_event((SDLKey)keymap.fire, UP);
+
+      Menu::set_current(game_menu);
+      st_pause_ticks_start();
+    }
+}
+
+void
+GameSession::process_events()
+{
+  if (end_sequence != NO_ENDSEQUENCE)
+    {
+      Player& tux = *currentsector->player;
+         
+      tux.input.fire  = UP;
+      tux.input.left  = UP;
+      tux.input.right = DOWN;
+      tux.input.down  = UP; 
+
+      if (int(last_x_pos) == int(tux.base.x))
+        tux.input.up    = DOWN; 
+      else
+        tux.input.up    = UP; 
+
+      last_x_pos = tux.base.x;
 
-      switch(event.type)
+      SDL_Event event;
+      while (SDL_PollEvent(&event))
         {
-        case SDL_QUIT:        /* Quit event - quit: */
-          quit = true;
-          break;
-        case SDL_KEYDOWN:     /* A keypress! */
-          {
-            SDLKey key = event.key.keysym.sym;
-            
-            if(tux.key_event(key,DOWN))
-              break;
+          /* Check for menu-events, if the menu is shown */
+          if (Menu::current())
+            {
+              Menu::current()->event(event);
+             if(!Menu::current())
+             st_pause_ticks_stop();
+            }
 
-            switch(key)
+          switch(event.type)
+            {
+            case SDL_QUIT:        /* Quit event - quit: */
+              st_abort("Received window close", "");
+              break;
+              
+            case SDL_KEYDOWN:     /* A keypress! */
               {
-              case SDLK_ESCAPE:    /* Escape: Open/Close the menu: */
-                if(!game_pause)
+                SDLKey key = event.key.keysym.sym;
+           
+                switch(key)
                   {
-                    if(st_gl_mode == ST_GL_TEST)
-                      quit = true;
-                    else if(show_menu)
-                      {
-                        Menu::set_current(game_menu);
-                        show_menu = 0;
-                        st_pause_ticks_stop();
-                      }
-                    else
-                      {
-                        Menu::set_current(game_menu);
-                        show_menu = 1;
-                        st_pause_ticks_start();
-                      }
+                  case SDLK_ESCAPE:    /* Escape: Open/Close the menu: */
+                    on_escape_press();
+                    break;
+                  default:
+                    break;
                   }
-                break;
-              default:
-                break;
               }
-          }
-          break;
-        case SDL_KEYUP:      /* A keyrelease! */
-          {
-            SDLKey key = event.key.keysym.sym;
-
-            if(tux.key_event(key, UP))
+          
+            case SDL_JOYBUTTONDOWN:
+              if (event.jbutton.button == joystick_keymap.start_button)
+                on_escape_press();
               break;
+            }
+        }
+    }
+  else // normal mode
+    {
+      if(!Menu::current() && !game_pause)
+        st_pause_ticks_stop();
 
-            switch(key)
-              {
-              case SDLK_p:
-                if(!show_menu)
+      SDL_Event event;
+      while (SDL_PollEvent(&event))
+        {
+          /* Check for menu-events, if the menu is shown */
+          if (Menu::current())
+            {
+              Menu::current()->event(event);
+              if(!Menu::current())
+                st_pause_ticks_stop();
+            }
+          else
+            {
+              Player& tux = *currentsector->player;
+  
+              switch(event.type)
+                {
+                case SDL_QUIT:        /* Quit event - quit: */
+                  st_abort("Received window close", "");
+                  break;
+
+                case SDL_KEYDOWN:     /* A keypress! */
                   {
-                    if(game_pause)
-                      {
-                        game_pause = false;
-                        st_pause_ticks_stop();
-                      }
-                    else
+                    SDLKey key = event.key.keysym.sym;
+            
+                    if(tux.key_event(key,DOWN))
+                      break;
+
+                    switch(key)
                       {
-                        game_pause = true;
-                        st_pause_ticks_start();
+                      case SDLK_ESCAPE:    /* Escape: Open/Close the menu: */
+                        on_escape_press();
+                        break;
+                      default:
+                        break;
                       }
                   }
-                break;
-              case SDLK_TAB:
-                if(debug_mode)
+                  break;
+                case SDL_KEYUP:      /* A keyrelease! */
                   {
-                    tux.size = !tux.size;
-                    if(tux.size == BIG)
+                    SDLKey key = event.key.keysym.sym;
+
+                    if(tux.key_event(key, UP))
+                      break;
+
+                    switch(key)
                       {
-                        tux.base.height = 64;
+                      case SDLK_a:
+                        if(debug_mode)
+                        {
+                          char buf[160];
+                          snprintf(buf, sizeof(buf), "P: %4.1f,%4.1f",
+                              tux.base.x, tux.base.y);
+                          context->draw_text(white_text, buf,
+                              Vector(0, screen->h - white_text->get_height()),
+                              LAYER_FOREGROUND1);
+                          context->do_drawing();
+                          SDL_Delay(1000);
+                        }
+                        break;
+                      case SDLK_p:
+                        if(!Menu::current())
+                          {
+                            if(game_pause)
+                              {
+                                game_pause = false;
+                                st_pause_ticks_stop();
+                              }
+                            else
+                              {
+                                game_pause = true;
+                                st_pause_ticks_start();
+                              }
+                          }
+                        break;
+                      case SDLK_TAB:
+                        if(debug_mode)
+                          {
+                            tux.grow(false);
+                          }
+                        break;
+                      case SDLK_END:
+                        if(debug_mode)
+                          player_status.distros += 50;
+                        break;
+                      case SDLK_DELETE:
+                        if(debug_mode)
+                          tux.got_power = tux.FIRE_POWER;
+                        break;
+                      case SDLK_HOME:
+                        if(debug_mode)
+                          tux.got_power = tux.ICE_POWER;
+                        break;
+                      case SDLK_INSERT:
+                        if(debug_mode)
+                          tux.invincible_timer.start(TUX_INVINCIBLE_TIME);
+                        break;
+                      case SDLK_l:
+                        if(debug_mode)
+                          --player_status.lives;
+                        break;
+                      case SDLK_s:
+                        if(debug_mode)
+                          player_status.score += 1000;
+                      case SDLK_f:
+                        if(debug_fps)
+                          debug_fps = false;
+                        else
+                          debug_fps = true;
+                        break;
+                      default:
+                        break;
                       }
-                    else
-                      tux.base.height = 32;
                   }
-                break;
-              case SDLK_END:
-                if(debug_mode)
-                  distros += 50;
-                break;
-              case SDLK_SPACE:
-                if(debug_mode)
-                  next_level = 1;
-                break;
-              case SDLK_DELETE:
-                if(debug_mode)
-                  tux.got_coffee = 1;
-                break;
-              case SDLK_INSERT:
-                if(debug_mode)
-                  timer_start(&tux.invincible_timer,TUX_INVINCIBLE_TIME);
-                break;
-              case SDLK_l:
-                if(debug_mode)
-                  --tux.lives;
-                break;
-              case SDLK_s:
-                if(debug_mode)
-                  score += 1000;
-              case SDLK_f:
-                if(debug_fps)
-                  debug_fps = false;
-                else
-                  debug_fps = true;
-                break;
-              default:
-                break;
-              }
-          }
-          break;
+                  break;
 
-        case SDL_JOYAXISMOTION:
-          switch(event.jaxis.axis)
-            {
-            case JOY_X:
-              if (event.jaxis.value < -JOYSTICK_DEAD_ZONE)
-                {
-                  tux.input.left  = DOWN;
-                  tux.input.right = UP;
-                }
-              else if (event.jaxis.value > JOYSTICK_DEAD_ZONE)
-                {
-                  tux.input.left  = UP;
-                  tux.input.right = DOWN;
-                }
-              else
-                {
-                  tux.input.left  = DOWN;
-                  tux.input.right = DOWN;
-                }
-              break;
-            case JOY_Y:
-              if (event.jaxis.value > JOYSTICK_DEAD_ZONE)
-                tux.input.down = DOWN;
-              else if (event.jaxis.value < -JOYSTICK_DEAD_ZONE)
-                tux.input.down = UP;
-              else
-                tux.input.down = UP;
-              
-             break;
-            default:
-              break;
+                case SDL_JOYAXISMOTION:
+                  if (event.jaxis.axis == joystick_keymap.x_axis)
+                    {
+                      if (event.jaxis.value < -joystick_keymap.dead_zone)
+                        {
+                          tux.input.left  = DOWN;
+                          tux.input.right = UP;
+                        }
+                      else if (event.jaxis.value > joystick_keymap.dead_zone)
+                        {
+                          tux.input.left  = UP;
+                          tux.input.right = DOWN;
+                        }
+                      else
+                        {
+                          tux.input.left  = DOWN;
+                          tux.input.right = DOWN;
+                        }
+                    }
+                  else if (event.jaxis.axis == joystick_keymap.y_axis)
+                    {
+                      if (event.jaxis.value > joystick_keymap.dead_zone)
+                        tux.input.down = DOWN;
+                      else if (event.jaxis.value < -joystick_keymap.dead_zone)
+                        tux.input.down = UP;
+                      else
+                        tux.input.down = UP;
+                    }
+                  break;
+            
+                case SDL_JOYBUTTONDOWN:
+                  if (event.jbutton.button == joystick_keymap.a_button)
+                    tux.input.up = DOWN;
+                  else if (event.jbutton.button == joystick_keymap.b_button)
+                    tux.input.fire = DOWN;
+                  else if (event.jbutton.button == joystick_keymap.start_button)
+                    on_escape_press();
+                  break;
+                case SDL_JOYBUTTONUP:
+                  if (event.jbutton.button == joystick_keymap.a_button)
+                    tux.input.up = UP;
+                  else if (event.jbutton.button == joystick_keymap.b_button)
+                    tux.input.fire = UP;
+                  break;
+
+                default:
+                  break;
+                }  /* switch */
             }
-          break;
-        case SDL_JOYBUTTONDOWN:
-          if (event.jbutton.button == JOY_A)
-            tux.input.up = DOWN;
-          else if (event.jbutton.button == JOY_B)
-            tux.input.fire = DOWN;
-          break;
-        case SDL_JOYBUTTONUP:
-          if (event.jbutton.button == JOY_A)
-            tux.input.up = UP;
-          else if (event.jbutton.button == JOY_B)
-            tux.input.fire = UP;
-           
-          break;
-
-        default:
-          break;
-
-        }  /* switch */
-
-    } /* while */
+        } /* while */
+    }
 }
 
-int
-GameSession::action()
+void
+GameSession::check_end_conditions()
 {
-  Player& tux = *world->get_tux();
-
-  if (tux.is_dead() || next_level)
-    {
-      /* Tux either died, or reached the end of a level! */
-      halt_music();
-      
-      if (next_level)
-        {
-          /* End of a level! */
-          level++;
-          next_level = 0;
-          if(st_gl_mode != ST_GL_TEST)
-            {
-              drawresultscreen();
-            }
-          else
-            {
-              world->get_level()->free_gfx();
-              world->get_level()->cleanup();
-              world->get_level()->free_song();
-              world->arrays_free();
-
-              unloadshared();
-              return(0);
-            }
-          tux.level_begin();
-        }
-      else
-        {
-          tux.is_dying();
-
-          /* No more lives!? */
-
-          if (tux.lives < 0)
-            {
-              if(st_gl_mode != ST_GL_TEST)
-                drawendscreen();
+  Player* tux = currentsector->player;
 
-              if(st_gl_mode != ST_GL_TEST)
-                {
-                  if (score > hs_score)
-                    save_hs(score);
-                }
-
-              world->get_level()->free_gfx();
-              world->get_level()->cleanup();
-              world->get_level()->free_song();
-              world->arrays_free();
-
-              unloadshared();
-              return(0);
-            } /* if (lives < 0) */
-        }
+  /* End of level? */
+  Tile* endtile = collision_goal(tux->base);
 
-      /* Either way, (re-)load the (next) level... */
-      tux.level_begin();
-      world->set_defaults();
-      
-      world->get_level()->cleanup();
+  if(end_sequence && !endsequence_timer.check())
+    {
+      exit_status = ES_LEVEL_FINISHED;
+      return;
+    }
+  else if(end_sequence == ENDSEQUENCE_RUNNING && endtile && endtile->data >= 1)
+    {
+      end_sequence = ENDSEQUENCE_WAITING;
+    }
+  else if(!end_sequence && endtile && endtile->data == 0)
+    {
+      end_sequence = ENDSEQUENCE_RUNNING;
+      last_x_pos = -1;
+      music_manager->play_music(level_end_song, 0);
+      endsequence_timer.start(7000); // 5 seconds until we finish the map
+      tux->invincible_timer.start(7000); //FIXME: Implement a winning timer for the end sequence (with special winning animation etc.)
+    }
+  else if (!end_sequence && tux->is_dead())
+    {
+      player_status.bonus = PlayerStatus::NO_BONUS;
 
-      if (st_gl_mode == ST_GL_LOAD_LEVEL_FILE)
-        {
-          if(world->get_level()->load(subset) != 0)
-            return 0;
+      if (player_status.lives < 0)
+        { // No more lives!?
+          exit_status = ES_GAME_OVER;
         }
       else
-        {
-          if(world->get_level()->load(subset, level) != 0)
-            return 0;
+        { // Still has lives, so reset Tux to the levelstart
+          restart_level();
         }
 
-      world->arrays_free();
-      world->activate_bad_guys();
-      world->activate_particle_systems();
-
-      world->get_level()->free_gfx();
-      world->get_level()->load_gfx();
-      world->get_level()->free_song();
-      world->get_level()->load_song();
-
-      if(st_gl_mode != ST_GL_TEST)
-        levelintro();
-      start_timers();
-      /* Play music: */
-      play_current_music();
+      return;
     }
+}
 
-  tux.action();
-
-  world->action();
+void
+GameSession::action(double frame_ratio)
+{
+  if (exit_status == ES_NONE && !currentsector->player->growing_timer.check())
+    {
+      // Update Tux and the World
+      currentsector->action(frame_ratio);
+    }
 
-  return -1;
+  // respawning in new sector?
+  if(newsector != "" && newspawnpoint != "") {
+    Sector* sector = level->get_sector(newsector);
+    currentsector = sector;
+    currentsector->activate(newspawnpoint);
+    currentsector->play_music(LEVEL_MUSIC);
+    newsector = newspawnpoint = "";
+  }
 }
 
 void 
 GameSession::draw()
 {
-  world->draw();
-  drawstatus();
+  currentsector->draw(*context);
+  drawstatus(*context);
 
   if(game_pause)
     {
       int x = screen->h / 20;
       for(int i = 0; i < x; ++i)
         {
-          fillrect(i % 2 ? (pause_menu_frame * i)%screen->w : -((pause_menu_frame * i)%screen->w) ,(i*20+pause_menu_frame)%screen->h,screen->w,10,20,20,20, rand() % 20 + 1);
+          context->draw_filled_rect(
+              Vector(i % 2 ? (pause_menu_frame * i)%screen->w :
+                -((pause_menu_frame * i)%screen->w)
+                ,(i*20+pause_menu_frame)%screen->h),
+              Vector(screen->w,10),
+              Color(20,20,20, rand() % 20 + 1), LAYER_FOREGROUND1+1);
         }
-      fillrect(0,0,screen->w,screen->h,rand() % 50, rand() % 50, rand() % 50, 128);
-      text_drawf(&blue_text, "PAUSE - Press 'P' To Play", 0, 230, A_HMIDDLE, A_TOP, 1);
+      context->draw_filled_rect(
+          Vector(0,0), Vector(screen->w, screen->h),
+          Color(rand() % 50, rand() % 50, rand() % 50, 128), LAYER_FOREGROUND1);
+      context->draw_text_center(blue_text, "PAUSE - Press 'P' To Play",
+          Vector(0, 230), LAYER_FOREGROUND1+2);
     }
 
-  if(show_menu)
+  if(Menu::current())
     {
-      menu_process_current();
-      mouse_cursor->draw();
+      Menu::current()->draw(*context);
+      mouse_cursor->draw(*context);
     }
 
-  updatescreen();
+  context->do_drawing();
 }
 
+void
+GameSession::process_menu()
+{
+  Menu* menu = Menu::current();
+  if(menu)
+    {
+      menu->action();
 
-int
+      if(menu == game_menu)
+        {
+          switch (game_menu->check())
+            {
+            case MNID_CONTINUE:
+              st_pause_ticks_stop();
+              break;
+            case MNID_ABORTLEVEL:
+              st_pause_ticks_stop();
+              exit_status = ES_LEVEL_ABORT;
+              break;
+            }
+        }
+      else if(menu == options_menu)
+        {
+          process_options_menu();
+        }
+      else if(menu == load_game_menu )
+        {
+          process_load_game_menu();
+        }
+    }
+}
+
+GameSession::ExitStatus
 GameSession::run()
 {
-  Player& tux = *world->get_tux();
+  Menu::set_current(0);
   current_ = this;
   
-  int  fps_cnt;
-  bool done;
-
-  global_frame_counter = 0;
-  game_pause = false;
-  timer_init(&fps_timer,true);
-  timer_init(&frame_timer,true);
-  last_update_time = st_get_ticks();
-  fps_cnt = 0;
+  int fps_cnt = 0;
 
-  /* Clear screen: */
-  clearscreen(0, 0, 0);
-  updatescreen();
-
-  /* Play music: */
-  play_current_music();
+  update_time = last_update_time = st_get_ticks();
 
   // Eat unneeded events
   SDL_Event event;
@@ -491,78 +581,35 @@ GameSession::run()
 
   draw();
 
-  done = false;
-  quit = false;
-  while (!done && !quit)
+  while (exit_status == ES_NONE)
     {
       /* Calculate the movement-factor */
-      frame_ratio = ((double)(update_time-last_update_time))/((double)FRAME_RATE);
-      if(frame_ratio > 1.5) /* Quick hack to correct the unprecise CPU clocks a little bit. */
-        frame_ratio = 1.5 + (frame_ratio - 1.5) * 0.85;
+      double frame_ratio = ((double)(update_time-last_update_time))/((double)FRAME_RATE);
 
-      if(!timer_check(&frame_timer))
+      if(!frame_timer.check())
         {
-          timer_start(&frame_timer,25);
+          frame_timer.start(25);
           ++global_frame_counter;
         }
 
       /* Handle events: */
-      tux.input.old_fire = tux.input.fire;
+      currentsector->player->input.old_fire 
+        = currentsector->player->input.fire;
 
       process_events();
+      process_menu();
 
-      if(show_menu)
-        {
-          if(current_menu == game_menu)
-            {
-              switch (game_menu->check())
-                {
-                case 2:
-                  st_pause_ticks_stop();
-                  break;
-                case 3:
-                  update_load_save_game_menu(save_game_menu, false);
-                  break;
-                case 4:
-                  update_load_save_game_menu(load_game_menu, true);
-                  break;
-                case 7:
-                  st_pause_ticks_stop();
-                  done = true;
-                  break;
-                }
-            }
-          else if(current_menu == options_menu)
-            {
-              process_options_menu();
-            }
-          else if(current_menu == save_game_menu )
-            {
-              process_save_game_menu();
-            }
-          else if(current_menu == load_game_menu )
-            {
-              process_load_game_menu();
-            }
-        }
-
-
-      /* Handle actions: */
-
-      if(!game_pause && !show_menu)
+      // Update the world state and all objects in the world
+      // Do that with a constante time-delta so that the game will run
+      // determistic and not different on different machines
+      if(!game_pause && !Menu::current())
         {
-          /*float z = frame_ratio;
-            frame_ratio = 1;
-            while(z >= 1)
-            {*/
-          if (action() == 0)
-            {
-              /* == 0: no more lives */
-              /* == -1: continues */
-              return 0;
-            }
-          /*  --z;
-                     }*/
+          // Update the world
+          check_end_conditions();
+          if (end_sequence == ENDSEQUENCE_RUNNING)
+             action(frame_ratio/2);
+          else if(end_sequence == NO_ENDSEQUENCE)
+             action(frame_ratio);
         }
       else
         {
@@ -570,156 +617,140 @@ GameSession::run()
           SDL_Delay(50);
         }
 
-      if(debug_mode && debug_fps)
-        SDL_Delay(60);
-
-      /*Draw the current scene to the screen */
-      /*If the machine running the game is too slow
-        skip the drawing of the frame (so the calculations are more precise and
-        the FPS aren't affected).*/
-      /*if( ! fps_fps < 50.0 )
-        game_draw();
-        else
-        jump = true;*/ /*FIXME: Implement this tweak right.*/
       draw();
 
       /* Time stops in pause mode */
-      if(game_pause || show_menu )
+      if(game_pause || Menu::current())
         {
           continue;
         }
 
       /* Set the time of the last update and the time of the current update */
       last_update_time = update_time;
-      update_time = st_get_ticks();
+      update_time      = st_get_ticks();
 
       /* Pause till next frame, if the machine running the game is too fast: */
       /* FIXME: Works great for in OpenGl mode, where the CPU doesn't have to do that much. But
          the results in SDL mode aren't perfect (thought the 100 FPS are reached), even on an AMD2500+. */
-      if(last_update_time >= update_time - 12) {
-        SDL_Delay(10);
-        update_time = st_get_ticks();
-      }
-      /*if((update_time - last_update_time) < 10)
-        SDL_Delay((11 - (update_time - last_update_time))/2);*/
+      if(last_update_time >= update_time - 12) 
+        {
+          SDL_Delay(10);
+          update_time = st_get_ticks();
+        }
 
       /* Handle time: */
-      if (timer_check(&time_left))
-        {
-          /* are we low on time ? */
-          if ((timer_get_left(&time_left) < TIME_WARNING)
-              && (get_current_music() != HURRYUP_MUSIC))     /* play the fast music */
-            {
-              set_current_music(HURRYUP_MUSIC);
-              play_current_music();
-            }
+      if (!time_left.check() && currentsector->player->dying == DYING_NOT
+              && !end_sequence)
+        currentsector->player->kill(Player::KILL);
 
+      /* Handle music: */
+      if(currentsector->player->invincible_timer.check() && !end_sequence)
+        {
+          currentsector->play_music(HERRING_MUSIC);
+        }
+      /* are we low on time ? */
+      else if (time_left.get_left() < TIME_WARNING && !end_sequence)
+        {
+          currentsector->play_music(HURRYUP_MUSIC);
+        }
+      /* or just normal music? */
+      else if(currentsector->get_music_type() != LEVEL_MUSIC && !end_sequence)
+        {
+          currentsector->play_music(LEVEL_MUSIC);
         }
-      else
-        tux.kill(KILL);
 
       /* Calculate frames per second */
       if(show_fps)
         {
           ++fps_cnt;
-          fps_fps = (1000.0 / (float)timer_get_gone(&fps_timer)) * (float)fps_cnt;
+          fps_fps = (1000.0 / (float)fps_timer.get_gone()) * (float)fps_cnt;
 
-          if(!timer_check(&fps_timer))
+          if(!fps_timer.check())
             {
-              timer_start(&fps_timer,1000);
+              fps_timer.start(1000);
               fps_cnt = 0;
             }
         }
     }
+  
+  return exit_status;
+}
 
-  halt_music();
-
-  world->get_level()->free_gfx();
-  world->get_level()->cleanup();
-  world->get_level()->free_song();
-
-  unloadshared();
-  world->arrays_free();
-
-  return quit;
+void
+GameSession::respawn(const std::string& sector, const std::string& spawnpoint)
+{
+  newsector = sector;
+  newspawnpoint = spawnpoint;
 }
 
 /* Bounce a brick: */
 void bumpbrick(float x, float y)
 {
-  World::current()->add_bouncy_brick(((int)(x + 1) / 32) * 32,
-                         (int)(y / 32) * 32);
+  Sector::current()->add_bouncy_brick(Vector(((int)(x + 1) / 32) * 32,
+                         (int)(y / 32) * 32));
 
   play_sound(sounds[SND_BRICK], SOUND_CENTER_SPEAKER);
 }
 
 /* (Status): */
 void
-GameSession::drawstatus()
+GameSession::drawstatus(DrawingContext& context)
 {
-  Player& tux = *world->get_tux();
   char str[60];
+  
+  snprintf(str, 60, "%d", player_status.score);
+  context.draw_text(white_text, "SCORE", Vector(0, 0), LAYER_FOREGROUND1);
+  context.draw_text(gold_text, str, Vector(96, 0), LAYER_FOREGROUND1);
 
-  sprintf(str, "%d", score);
-  text_draw(&white_text, "SCORE", 0, 0, 1);
-  text_draw(&gold_text, str, 96, 0, 1);
-
-  if(st_gl_mode != ST_GL_TEST)
+  if(st_gl_mode == ST_GL_TEST)
     {
-      sprintf(str, "%d", hs_score);
-      text_draw(&white_text, "HIGH", 0, 20, 1);
-      text_draw(&gold_text, str, 96, 20, 1);
+      context.draw_text(white_text, "Press ESC To Return", Vector(0,20),
+          LAYER_FOREGROUND1);
     }
-  else
+
+  if(!time_left.check()) {
+    context.draw_text_center(white_text, "TIME's UP", Vector(0, 0),
+        LAYER_FOREGROUND1);
+  } else if (time_left.get_left() > TIME_WARNING || (global_frame_counter % 10) < 5) {
+    sprintf(str, "%d", time_left.get_left() / 1000 );
+    context.draw_text_center(white_text, "TIME",
+        Vector(0, 0), LAYER_FOREGROUND1);
+    context.draw_text_center(gold_text, str,
+        Vector(4*16, 0), LAYER_FOREGROUND1);
+  }
+
+  sprintf(str, "%d", player_status.distros);
+  context.draw_text(white_text, "COINS",
+      Vector(screen->w - white_text->w*9, 0), LAYER_FOREGROUND1);
+  context.draw_text(gold_text, str,
+      Vector(screen->w - gold_text->w*2, 0), LAYER_FOREGROUND1);
+
+  context.draw_text(white_text, "LIVES",
+      Vector(screen->w - white_text->w*9, 20), LAYER_FOREGROUND1);
+  if (player_status.lives >= 5)
     {
-      text_draw(&white_text,"Press ESC To Return",0,20,1);
+      sprintf(str, "%dx", player_status.lives);
+      float x = screen->w - gold_text->get_text_width(str) - tux_life->w;
+      context.draw_text(gold_text, str, Vector(x, 20), LAYER_FOREGROUND1);
+      context.draw_surface(tux_life, Vector(screen->w - 16, 20),
+          LAYER_FOREGROUND1);
     }
-
-  if (timer_get_left(&time_left) > TIME_WARNING || (global_frame_counter % 10) < 5)
+  else
     {
-      sprintf(str, "%d", timer_get_left(&time_left) / 1000 );
-      text_draw(&white_text, "TIME", 224, 0, 1);
-      text_draw(&gold_text, str, 304, 0, 1);
+      for(int i= 0; i < player_status.lives; ++i)
+        context.draw_surface(tux_life, 
+            Vector(screen->w - tux_life->w*4 +(tux_life->w*i), 20),
+            LAYER_FOREGROUND1);
     }
 
-  sprintf(str, "%d", distros);
-  text_draw(&white_text, "DISTROS", screen->h, 0, 1);
-  text_draw(&gold_text, str, 608, 0, 1);
-
-  text_draw(&white_text, "LIVES", screen->h, 20, 1);
-
   if(show_fps)
     {
       sprintf(str, "%2.1f", fps_fps);
-      text_draw(&white_text, "FPS", screen->h, 40, 1);
-      text_draw(&gold_text, str, screen->h + 60, 40, 1);
+      context.draw_text(white_text, "FPS", 
+          Vector(screen->w - white_text->w*9, 40), LAYER_FOREGROUND1);
+      context.draw_text(gold_text, str,
+          Vector(screen->w-4*16, 40), LAYER_FOREGROUND1);
     }
-
-  for(int i= 0; i < tux.lives; ++i)
-    {
-      texture_draw(&tux_life,565+(18*i),20);
-    }
-}
-
-void
-GameSession::drawendscreen()
-{
-  char str[80];
-
-  clearscreen(0, 0, 0);
-
-  text_drawf(&blue_text, "GAMEOVER", 0, 200, A_HMIDDLE, A_TOP, 1);
-
-  sprintf(str, "SCORE: %d", score);
-  text_drawf(&gold_text, str, 0, 224, A_HMIDDLE, A_TOP, 1);
-
-  sprintf(str, "DISTROS: %d", distros);
-  text_drawf(&gold_text, str, 0, 256, A_HMIDDLE, A_TOP, 1);
-
-  flipscreen();
-  
-  SDL_Event event;
-  wait_for_event(event,2000,5000,true);
 }
 
 void
@@ -727,145 +758,50 @@ GameSession::drawresultscreen(void)
 {
   char str[80];
 
-  clearscreen(0, 0, 0);
+  DrawingContext context;
+  currentsector->background->draw(context);  
 
-  text_drawf(&blue_text, "Result:", 0, 200, A_HMIDDLE, A_TOP, 1);
+  context.draw_text_center(blue_text, "Result:", Vector(0, 200),
+      LAYER_FOREGROUND1);
 
-  sprintf(str, "SCORE: %d", score);
-  text_drawf(&gold_text, str, 0, 224, A_HMIDDLE, A_TOP, 1);
+  sprintf(str, "SCORE: %d", player_status.score);
+  context.draw_text_center(gold_text, str, Vector(0, 224), LAYER_FOREGROUND1);
 
-  sprintf(str, "DISTROS: %d", distros);
-  text_drawf(&gold_text, str, 0, 256, A_HMIDDLE, A_TOP, 1);
+  sprintf(str, "COINS: %d", player_status.distros);
+  context.draw_text_center(gold_text, str, Vector(0, 256), LAYER_FOREGROUND1);
 
-  flipscreen();
+  context.do_drawing();
   
   SDL_Event event;
   wait_for_event(event,2000,5000,true);
 }
 
-void
-GameSession::savegame(int)
-{
-#if 0
-  char savefile[1024];
-  FILE* fi;
-  unsigned int ui;
-
-  sprintf(savefile,"%s/slot%d.save",st_save_dir,slot);
-
-  fi = fopen(savefile, "wb");
-
-  if (fi == NULL)
-    {
-      fprintf(stderr, "Warning: I could not open the slot file ");
-    }
-  else
-    {
-      fputs(level_subset, fi);
-      fputs("\n", fi);
-      fwrite(&level,sizeof(int),1,fi);
-      fwrite(&score,sizeof(int),1,fi);
-      fwrite(&distros,sizeof(int),1,fi);
-      fwrite(&scroll_x,sizeof(float),1,fi);
-      //FIXME:fwrite(&tux,sizeof(Player),1,fi);
-      //FIXME:timer_fwrite(&tux.invincible_timer,fi);
-      //FIXME:timer_fwrite(&tux.skidding_timer,fi);
-      //FIXME:timer_fwrite(&tux.safe_timer,fi);
-      //FIXME:timer_fwrite(&tux.frame_timer,fi);
-      timer_fwrite(&time_left,fi);
-      ui = st_get_ticks();
-      fwrite(&ui,sizeof(int),1,fi);
-    }
-  fclose(fi);
-#endif 
-}
-
-void
-GameSession::loadgame(int)
-{
-#if 0
-  char savefile[1024];
-  char str[100];
-  FILE* fi;
-  unsigned int ui;
-
-  sprintf(savefile,"%s/slot%d.save",st_save_dir,slot);
-
-  fi = fopen(savefile, "rb");
-
-  if (fi == NULL)
-    {
-      fprintf(stderr, "Warning: I could not open the slot file ");
-
-    }
-  else
-    {
-      fgets(str, 100, fi);
-      strcpy(level_subset, str);
-      level_subset[strlen(level_subset)-1] = '\0';
-      fread(&level,sizeof(int),1,fi);
-
-      world->set_defaults();
-      world->get_level()->cleanup();
-      world->arrays_free();
-      world->get_level()->free_gfx();
-      world->get_level()->free_song();
-
-      if(world->get_level()->load(level_subset,level) != 0)
-        exit(1);
-
-      world->activate_bad_guys();
-      world->activate_particle_systems();
-      world->get_level()->load_gfx();
-      world->get_level()->load_song();
-
-      levelintro();
-      update_time = st_get_ticks();
-
-      fread(&score,   sizeof(int),1,fi);
-      fread(&distros, sizeof(int),1,fi);
-      fread(&scroll_x,sizeof(float),1,fi);
-      //FIXME:fread(&tux,     sizeof(Player), 1, fi);
-      //FIXME:timer_fread(&tux.invincible_timer,fi);
-      //FIXME:timer_fread(&tux.skidding_timer,fi);
-      //FIXME:timer_fread(&tux.safe_timer,fi);
-      //FIXME:timer_fread(&tux.frame_timer,fi);
-      timer_fread(&time_left,fi);
-      fread(&ui,sizeof(int),1,fi);
-      fclose(fi);
-    }
-#endif 
-}
-
 std::string slotinfo(int slot)
 {
-  FILE* fi;
+  char tmp[1024];
   char slotfile[1024];
-  char tmp[200];
-  char str[5];
-  int slot_level;
-  sprintf(slotfile,"%s/slot%d.save",st_save_dir,slot);
-
-  fi = fopen(slotfile, "rb");
+  std::string title;
+  sprintf(slotfile,"%s/slot%d.stsg",st_save_dir,slot);
 
-  sprintf(tmp,"Slot %d - ",slot);
-
-  if (fi == NULL)
+  lisp_object_t* savegame = lisp_read_from_file(slotfile);
+  if (savegame)
     {
-      strcat(tmp,"Free");
+      LispReader reader(lisp_cdr(savegame));
+      reader.read_string("title", title);
+      lisp_free(savegame);
     }
-  else
+
+  if (access(slotfile, F_OK) == 0)
     {
-      fgets(str, 100, fi);
-      str[strlen(str)-1] = '\0';
-      strcat(tmp, str);
-      strcat(tmp, " / Level:");
-      fread(&slot_level,sizeof(int),1,fi);
-      sprintf(str,"%d",slot_level);
-      strcat(tmp,str);
-      fclose(fi);
+      if (!title.empty())
+        snprintf(tmp,1024,"Slot %d - %s",slot, title.c_str());
+      else
+        snprintf(tmp, 1024,"Slot %d - Savegame",slot);
     }
+  else
+    sprintf(tmp,"Slot %d - Free",slot);
 
   return tmp;
 }
 
+