d04a952f5db1db507c00e12f9c12f064cc5c5e5e
[supertux.git] / src / gameloop.cpp
1 //  $Id$
2 // 
3 //  SuperTux
4 //  Copyright (C) 2000 Bill Kendrick <bill@newbreedsoftware.com>
5 //  Copyright (C) 2004 Tobias Glaesser <tobi.web@gmx.de>
6 //  Copyright (C) 2004 Ingo Ruhnke <grumbel@gmx.de>
7 //
8 //  This program is free software; you can redistribute it and/or
9 //  modify it under the terms of the GNU General Public License
10 //  as published by the Free Software Foundation; either version 2
11 //  of the License, or (at your option) any later version.
12 //
13 //  This program is distributed in the hope that it will be useful,
14 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
15 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 //  GNU General Public License for more details.
17 // 
18 //  You should have received a copy of the GNU General Public License
19 //  along with this program; if not, write to the Free Software
20 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
21
22 #include <iostream>
23 #include <cassert>
24 #include <cstdio>
25 #include <cstdlib>
26 #include <cmath>
27 #include <cstring>
28 #include <cerrno>
29 #include <unistd.h>
30 #include <ctime>
31
32 #include "SDL.h"
33
34 #ifndef WIN32
35 #include <sys/types.h>
36 #include <ctype.h>
37 #endif
38
39 #include "defines.h"
40 #include "app/globals.h"
41 #include "gameloop.h"
42 #include "video/screen.h"
43 #include "app/setup.h"
44 #include "high_scores.h"
45 #include "gui/menu.h"
46 #include "badguy.h"
47 #include "sector.h"
48 #include "special.h"
49 #include "player.h"
50 #include "level.h"
51 #include "scene.h"
52 #include "collision.h"
53 #include "tile.h"
54 #include "particlesystem.h"
55 #include "resources.h"
56 #include "background.h"
57 #include "tilemap.h"
58 #include "app/gettext.h"
59 #include "worldmap.h"
60 #include "intro.h"
61 #include "misc.h"
62
63 GameSession* GameSession::current_ = 0;
64
65 GameSession::GameSession(const std::string& levelname_, int mode, bool flip_level_)
66   : level(0), currentsector(0), st_gl_mode(mode),
67     end_sequence(NO_ENDSEQUENCE), levelname(levelname_), flip_level(flip_level_)
68 {
69   current_ = this;
70   
71   global_frame_counter = 0;
72   game_pause = false;
73   fps_fps = 0;
74
75   fps_timer.init(true);            
76   frame_timer.init(true);
77
78   context = new DrawingContext();
79
80   restart_level();
81 }
82
83 void
84 GameSession::restart_level()
85 {
86   game_pause   = false;
87   exit_status  = ES_NONE;
88   end_sequence = NO_ENDSEQUENCE;
89
90   fps_timer.init(true);
91   frame_timer.init(true);
92
93 #if 0
94   float old_x_pos = -1;
95   if (world)
96     { // Tux has lost a life, so we try to respawn him at the nearest reset point
97       old_x_pos = world->get_tux()->base.x;
98     }
99 #endif
100   
101   delete level;
102   currentsector = 0;
103
104   level = new Level;
105   level->load(levelname);
106   if(flip_level)
107     level->do_vertical_flip();
108   currentsector = level->get_sector("main");
109   if(!currentsector)
110     Termination::abort("Level has no main sector.", "");
111   currentsector->activate("main");
112
113 #if 0 // TODO
114   // Set Tux to the nearest reset point
115   if (old_x_pos != -1)
116     {
117       ResetPoint best_reset_point = { -1, -1 };
118       for(std::vector<ResetPoint>::iterator i = get_level()->reset_points.begin();
119           i != get_level()->reset_points.end(); ++i)
120         {
121           if (i->x < old_x_pos && best_reset_point.x < i->x)
122             best_reset_point = *i;
123         }
124       
125       if (best_reset_point.x != -1)
126         {
127           world->get_tux()->base.x = best_reset_point.x;
128           world->get_tux()->base.y = best_reset_point.y;
129         }
130     }
131 #endif
132     
133   if (st_gl_mode != ST_GL_DEMO_GAME)
134     {
135       if(st_gl_mode == ST_GL_PLAY || st_gl_mode == ST_GL_LOAD_LEVEL_FILE)
136         levelintro();
137     }
138
139   time_left.init(true);
140   start_timers();
141   currentsector->play_music(LEVEL_MUSIC);
142 }
143
144 GameSession::~GameSession()
145 {
146   delete level;
147   delete context;
148 }
149
150 void
151 GameSession::levelintro(void)
152 {
153   SoundManager::get()->halt_music();
154   
155   char str[60];
156
157   DrawingContext context;
158   currentsector->background->draw(context);
159
160   context.draw_text_center(gold_text, level->get_name(), Vector(0, 220),
161       LAYER_FOREGROUND1);
162
163   sprintf(str, "TUX x %d", player_status.lives);
164   context.draw_text_center(white_text, str, Vector(0, 240),
165       LAYER_FOREGROUND1);
166
167   if(level->get_author().size())
168     context.draw_text_center(white_small_text,
169       std::string(_("by ")) + level->get_author(), 
170       Vector(0, 400), LAYER_FOREGROUND1);
171
172
173   if(flip_level)
174     context.draw_text_center(white_text,
175       _("Level Vertically Flipped!"),
176       Vector(0, 310), LAYER_FOREGROUND1);
177
178   context.do_drawing();
179
180   SDL_Event event;
181   wait_for_event(event,1000,3000,true);
182 }
183
184 /* Reset Timers */
185 void
186 GameSession::start_timers()
187 {
188   time_left.start(level->time_left*1000);
189   Ticks::pause_init();
190   update_time = Ticks::get();
191 }
192
193 void
194 GameSession::on_escape_press()
195 {
196   if(currentsector->player->dying || end_sequence != NO_ENDSEQUENCE)
197     return;   // don't let the player open the menu, when he is dying
198   
199   if(game_pause)
200     return;
201
202   if(st_gl_mode == ST_GL_TEST)
203     {
204       exit_status = ES_LEVEL_ABORT;
205     }
206   else if (!Menu::current())
207     {
208       /* Tell Tux that the keys are all down, otherwise
209         it could have nasty bugs, like going allways to the right
210         or whatever that key does */
211       Player& tux = *(currentsector->player);
212       tux.key_event((SDLKey)keymap.jump, UP);
213       tux.key_event((SDLKey)keymap.duck, UP);
214       tux.key_event((SDLKey)keymap.left, UP);
215       tux.key_event((SDLKey)keymap.right, UP);
216       tux.key_event((SDLKey)keymap.fire, UP);
217
218       Menu::set_current(game_menu);
219       Ticks::pause_start();
220     }
221 }
222
223 void
224 GameSession::process_events()
225 {
226   if (end_sequence != NO_ENDSEQUENCE)
227     {
228       Player& tux = *currentsector->player;
229          
230       tux.input.fire  = UP;
231       tux.input.left  = UP;
232       tux.input.right = DOWN;
233       tux.input.down  = UP; 
234
235       if (int(last_x_pos) == int(tux.base.x))
236         tux.input.up    = DOWN; 
237       else
238         tux.input.up    = UP; 
239
240       last_x_pos = tux.base.x;
241
242       SDL_Event event;
243       while (SDL_PollEvent(&event))
244         {
245           /* Check for menu-events, if the menu is shown */
246           if (Menu::current())
247             {
248               Menu::current()->event(event);
249               if(!Menu::current())
250               Ticks::pause_stop();
251             }
252
253           switch(event.type)
254             {
255             case SDL_QUIT:        /* Quit event - quit: */
256               Termination::abort("Received window close", "");
257               break;
258               
259             case SDL_KEYDOWN:     /* A keypress! */
260               {
261                 SDLKey key = event.key.keysym.sym;
262            
263                 switch(key)
264                   {
265                   case SDLK_ESCAPE:    /* Escape: Open/Close the menu: */
266                     on_escape_press();
267                     break;
268                   default:
269                     break;
270                   }
271               }
272           
273             case SDL_JOYBUTTONDOWN:
274               if (event.jbutton.button == joystick_keymap.start_button)
275                 on_escape_press();
276               break;
277             }
278         }
279     }
280   else // normal mode
281     {
282       if(!Menu::current() && !game_pause)
283         Ticks::pause_stop();
284
285       SDL_Event event;
286       while (SDL_PollEvent(&event))
287         {
288           /* Check for menu-events, if the menu is shown */
289           if (Menu::current())
290             {
291               Menu::current()->event(event);
292               if(!Menu::current())
293                 Ticks::pause_stop();
294             }
295           else
296             {
297               Player& tux = *currentsector->player;
298   
299               switch(event.type)
300                 {
301                 case SDL_QUIT:        /* Quit event - quit: */
302                   Termination::abort("Received window close", "");
303                   break;
304
305                 case SDL_KEYDOWN:     /* A keypress! */
306                   {
307                     SDLKey key = event.key.keysym.sym;
308             
309                     if(tux.key_event(key,DOWN))
310                       break;
311
312                     switch(key)
313                       {
314                       case SDLK_ESCAPE:    /* Escape: Open/Close the menu: */
315                         on_escape_press();
316                         break;
317                       default:
318                         break;
319                       }
320                   }
321                   break;
322                 case SDL_KEYUP:      /* A keyrelease! */
323                   {
324                     SDLKey key = event.key.keysym.sym;
325
326                     if(tux.key_event(key, UP))
327                       break;
328
329                     switch(key)
330                       {
331                       case SDLK_a:
332                         if(debug_mode)
333                         {
334                           char buf[160];
335                           snprintf(buf, sizeof(buf), "P: %4.1f,%4.1f",
336                               tux.base.x, tux.base.y);
337                           context->draw_text(white_text, buf,
338                               Vector(0, screen->h - white_text->get_height()),
339                               LAYER_FOREGROUND1);
340                           context->do_drawing();
341                           SDL_Delay(1000);
342                         }
343                         break;
344                       case SDLK_p:
345                         if(!Menu::current())
346                           {
347                             if(game_pause)
348                               {
349                                 game_pause = false;
350                                 Ticks::pause_stop();
351                               }
352                             else
353                               {
354                                 game_pause = true;
355                                 Ticks::pause_start();
356                               }
357                           }
358                         break;
359                       case SDLK_TAB:
360                         if(debug_mode)
361                           {
362                             tux.grow(false);
363                           }
364                         break;
365                       case SDLK_END:
366                         if(debug_mode)
367                           player_status.distros += 50;
368                         break;
369                       case SDLK_DELETE:
370                         if(debug_mode)
371                           tux.got_power = tux.FIRE_POWER;
372                         break;
373                       case SDLK_HOME:
374                         if(debug_mode)
375                           tux.got_power = tux.ICE_POWER;
376                         break;
377                       case SDLK_INSERT:
378                         if(debug_mode)
379                           tux.invincible_timer.start(TUX_INVINCIBLE_TIME);
380                         break;
381                       case SDLK_l:
382                         if(debug_mode)
383                           --player_status.lives;
384                         break;
385                       case SDLK_s:
386                         if(debug_mode)
387                           player_status.score += 1000;
388                       case SDLK_f:
389                         if(debug_fps)
390                           debug_fps = false;
391                         else
392                           debug_fps = true;
393                         break;
394                       default:
395                         break;
396                       }
397                   }
398                   break;
399
400                 case SDL_JOYAXISMOTION:
401                   if (event.jaxis.axis == joystick_keymap.x_axis)
402                     {
403                       if (event.jaxis.value < -joystick_keymap.dead_zone)
404                         {
405                           tux.input.left  = DOWN;
406                           tux.input.right = UP;
407                         }
408                       else if (event.jaxis.value > joystick_keymap.dead_zone)
409                         {
410                           tux.input.left  = UP;
411                           tux.input.right = DOWN;
412                         }
413                       else
414                         {
415                           tux.input.left  = DOWN;
416                           tux.input.right = DOWN;
417                         }
418                     }
419                   else if (event.jaxis.axis == joystick_keymap.y_axis)
420                     {
421                       if (event.jaxis.value > joystick_keymap.dead_zone)
422                         tux.input.down = DOWN;
423                       else if (event.jaxis.value < -joystick_keymap.dead_zone)
424                         tux.input.down = UP;
425                       else
426                         tux.input.down = UP;
427                     }
428                   break;
429             
430                 case SDL_JOYBUTTONDOWN:
431                   if (event.jbutton.button == joystick_keymap.a_button)
432                     tux.input.up = DOWN;
433                   else if (event.jbutton.button == joystick_keymap.b_button)
434                     tux.input.fire = DOWN;
435                   else if (event.jbutton.button == joystick_keymap.start_button)
436                     on_escape_press();
437                   break;
438                 case SDL_JOYBUTTONUP:
439                   if (event.jbutton.button == joystick_keymap.a_button)
440                     tux.input.up = UP;
441                   else if (event.jbutton.button == joystick_keymap.b_button)
442                     tux.input.fire = UP;
443                   break;
444
445                 default:
446                   break;
447                 }  /* switch */
448             }
449         } /* while */
450     }
451 }
452
453 void
454 GameSession::check_end_conditions()
455 {
456   Player* tux = currentsector->player;
457
458   /* End of level? */
459   Tile* endtile = collision_goal(tux->base);
460
461   if(end_sequence && !endsequence_timer.check())
462     {
463       exit_status = ES_LEVEL_FINISHED;
464       return;
465     }
466   else if(end_sequence == ENDSEQUENCE_RUNNING && endtile && endtile->data >= 1)
467     {
468       end_sequence = ENDSEQUENCE_WAITING;
469     }
470   else if(!end_sequence && endtile && endtile->data == 0)
471     {
472       end_sequence = ENDSEQUENCE_RUNNING;
473       last_x_pos = -1;
474       SoundManager::get()->play_music(level_end_song, 0);
475       endsequence_timer.start(7000); // 5 seconds until we finish the map
476       tux->invincible_timer.start(7000); //FIXME: Implement a winning timer for the end sequence (with special winning animation etc.)
477     }
478   else if (!end_sequence && tux->is_dead())
479     {
480       player_status.bonus = PlayerStatus::NO_BONUS;
481
482       if (player_status.lives < 0)
483         { // No more lives!?
484           exit_status = ES_GAME_OVER;
485         }
486       else
487         { // Still has lives, so reset Tux to the levelstart
488           restart_level();
489         }
490
491       return;
492     }
493 }
494
495 void
496 GameSession::action(double frame_ratio)
497 {
498   if (exit_status == ES_NONE && !currentsector->player->growing_timer.check())
499     {
500       // Update Tux and the World
501       currentsector->action(frame_ratio);
502     }
503
504   // respawning in new sector?
505   if(newsector != "" && newspawnpoint != "") {
506     Sector* sector = level->get_sector(newsector);
507     currentsector = sector;
508     currentsector->activate(newspawnpoint);
509     currentsector->play_music(LEVEL_MUSIC);
510     newsector = newspawnpoint = "";
511   }
512 }
513
514 void 
515 GameSession::draw()
516 {
517   currentsector->draw(*context);
518   drawstatus(*context);
519
520   if(game_pause)
521     {
522       int x = screen->h / 20;
523       for(int i = 0; i < x; ++i)
524         {
525           context->draw_filled_rect(
526               Vector(i % 2 ? (pause_menu_frame * i)%screen->w :
527                 -((pause_menu_frame * i)%screen->w)
528                 ,(i*20+pause_menu_frame)%screen->h),
529               Vector(screen->w,10),
530               Color(20,20,20, rand() % 20 + 1), LAYER_FOREGROUND1+1);
531         }
532       context->draw_filled_rect(
533           Vector(0,0), Vector(screen->w, screen->h),
534           Color(rand() % 50, rand() % 50, rand() % 50, 128), LAYER_FOREGROUND1);
535       context->draw_text_center(blue_text, _("PAUSE - Press 'P' To Play"),
536           Vector(0, 230), LAYER_FOREGROUND1+2);
537
538       char str1[60];
539       char str2[124];
540       sprintf(str1, _("Playing: "));
541       sprintf(str2, level->name.c_str());
542
543       context->draw_text(blue_text, str1,
544           Vector((screen->w - (blue_text->get_text_width(str1) + white_text->get_text_width(str2)))/2, 340),
545           LAYER_FOREGROUND1+2);
546       context->draw_text(white_text, str2,
547           Vector(((screen->w - (blue_text->get_text_width(str1) + white_text->get_text_width(str2)))/2)+blue_text->get_text_width(str1), 340),
548           LAYER_FOREGROUND1+2);
549     }
550
551   if(Menu::current())
552     {
553       Menu::current()->draw(*context);
554       mouse_cursor->draw(*context);
555     }
556
557   context->do_drawing();
558 }
559
560 void
561 GameSession::process_menu()
562 {
563   Menu* menu = Menu::current();
564   if(menu)
565     {
566       menu->action();
567
568       if(menu == game_menu)
569         {
570           switch (game_menu->check())
571             {
572             case MNID_CONTINUE:
573               Ticks::pause_stop();
574               break;
575             case MNID_ABORTLEVEL:
576               Ticks::pause_stop();
577               exit_status = ES_LEVEL_ABORT;
578               break;
579             }
580         }
581       else if(menu == options_menu)
582         {
583           process_options_menu();
584         }
585       else if(menu == load_game_menu )
586         {
587           process_load_game_menu();
588         }
589     }
590 }
591
592 GameSession::ExitStatus
593 GameSession::run()
594 {
595   Menu::set_current(0);
596   current_ = this;
597   
598   int fps_cnt = 0;
599
600   update_time = last_update_time = Ticks::get();
601
602   // Eat unneeded events
603   SDL_Event event;
604   while (SDL_PollEvent(&event)) {}
605
606   draw();
607
608   while (exit_status == ES_NONE)
609     {
610       /* Calculate the movement-factor */
611       double frame_ratio = ((double)(update_time-last_update_time))/((double)FRAME_RATE);
612
613       if(!frame_timer.check())
614         {
615           frame_timer.start(25);
616           ++global_frame_counter;
617         }
618
619       /* Handle events: */
620       currentsector->player->input.old_fire 
621         = currentsector->player->input.fire;
622
623       process_events();
624       process_menu();
625
626       // Update the world state and all objects in the world
627       // Do that with a constante time-delta so that the game will run
628       // determistic and not different on different machines
629       if(!game_pause && !Menu::current())
630         {
631           // Update the world
632           check_end_conditions();
633           if (end_sequence == ENDSEQUENCE_RUNNING)
634              action(frame_ratio/2);
635           else if(end_sequence == NO_ENDSEQUENCE)
636              action(frame_ratio);
637         }
638       else
639         {
640           ++pause_menu_frame;
641           SDL_Delay(50);
642         }
643
644       draw();
645
646       /* Time stops in pause mode */
647       if(game_pause || Menu::current())
648         {
649           continue;
650         }
651
652       /* Set the time of the last update and the time of the current update */
653       last_update_time = update_time;
654       update_time      = Ticks::get();
655
656       /* Pause till next frame, if the machine running the game is too fast: */
657       /* FIXME: Works great for in OpenGl mode, where the CPU doesn't have to do that much. But
658          the results in SDL mode aren't perfect (thought the 100 FPS are reached), even on an AMD2500+. */
659       if(last_update_time >= update_time - 12) 
660         {
661           SDL_Delay(10);
662           update_time = Ticks::get();
663         }
664
665       /* Handle time: */
666       if (!time_left.check() && currentsector->player->dying == DYING_NOT
667               && !end_sequence)
668         currentsector->player->kill(Player::KILL);
669
670       /* Handle music: */
671       if(currentsector->player->invincible_timer.check() && !end_sequence)
672         {
673           currentsector->play_music(HERRING_MUSIC);
674         }
675       /* are we low on time ? */
676       else if (time_left.get_left() < TIME_WARNING && !end_sequence)
677         {
678           currentsector->play_music(HURRYUP_MUSIC);
679         }
680       /* or just normal music? */
681       else if(currentsector->get_music_type() != LEVEL_MUSIC && !end_sequence)
682         {
683           currentsector->play_music(LEVEL_MUSIC);
684         }
685
686       /* Calculate frames per second */
687       if(show_fps)
688         {
689           ++fps_cnt;
690           fps_fps = (1000.0 / (float)fps_timer.get_gone()) * (float)fps_cnt;
691
692           if(!fps_timer.check())
693             {
694               fps_timer.start(1000);
695               fps_cnt = 0;
696             }
697         }
698     }
699   
700   return exit_status;
701 }
702
703 void
704 GameSession::respawn(const std::string& sector, const std::string& spawnpoint)
705 {
706   newsector = sector;
707   newspawnpoint = spawnpoint;
708 }
709
710 /* Bounce a brick: */
711 void bumpbrick(float x, float y)
712 {
713   Sector::current()->add_bouncy_brick(Vector(((int)(x + 1) / 32) * 32,
714                          (int)(y / 32) * 32));
715
716   SoundManager::get()->play_sound(IDToSound(SND_BRICK), Vector(x, y), Sector::current()->player->get_pos());
717 }
718
719 /* (Status): */
720 void
721 GameSession::drawstatus(DrawingContext& context)
722 {
723   char str[60];
724   
725   snprintf(str, 60, " %d", player_status.score);
726   context.draw_text(white_text, _("SCORE"), Vector(0, 0), LAYER_FOREGROUND1);
727   context.draw_text(gold_text, str, Vector(96, 0), LAYER_FOREGROUND1);
728
729   if(st_gl_mode == ST_GL_TEST)
730     {
731       context.draw_text(white_text, _("Press ESC To Return"), Vector(0,20),
732           LAYER_FOREGROUND1);
733     }
734
735   if(!time_left.check()) {
736     context.draw_text_center(white_text, _("TIME's UP"), Vector(0, 0),
737         LAYER_FOREGROUND1);
738   } else if (time_left.get_left() > TIME_WARNING || (global_frame_counter % 10) < 5) {
739     sprintf(str, " %d", time_left.get_left() / 1000 );
740     context.draw_text_center(white_text, _("TIME"),
741         Vector(0, 0), LAYER_FOREGROUND1);
742     context.draw_text_center(gold_text, str,
743         Vector(4*16, 0), LAYER_FOREGROUND1);
744   }
745
746   sprintf(str, " %d", player_status.distros);
747   context.draw_text(white_text, _("COINS"),
748       Vector(screen->w - white_text->get_text_width(_("COINS"))-white_text->get_text_width("   99"), 0),
749         LAYER_FOREGROUND1);
750   context.draw_text(gold_text, str,
751       Vector(screen->w - gold_text->get_text_width(" 99"), 0),LAYER_FOREGROUND1);
752
753   if (player_status.lives >= 5)
754     {
755       sprintf(str, "%dx", player_status.lives);
756       float x = screen->w - gold_text->get_text_width(str) - tux_life->w;
757       context.draw_text(gold_text, str, Vector(x, 20), LAYER_FOREGROUND1);
758       context.draw_surface(tux_life, Vector(screen->w - 16, 20),
759           LAYER_FOREGROUND1);
760     }
761   else
762     {
763       for(int i= 0; i < player_status.lives; ++i)
764         context.draw_surface(tux_life, 
765             Vector(screen->w - tux_life->w*4 +(tux_life->w*i), 20),
766             LAYER_FOREGROUND1);
767     }
768
769   context.draw_text(white_text, _("LIVES"),
770       Vector(screen->w - white_text->get_text_width(_("LIVES")) - white_text->get_text_width("   99"), 20),
771       LAYER_FOREGROUND1);
772
773   if(show_fps)
774     {
775       sprintf(str, "%2.1f", fps_fps);
776       context.draw_text(white_text, "FPS", 
777           Vector(screen->w - white_text->get_text_width("FPS     "), 40),
778           LAYER_FOREGROUND1);
779       context.draw_text(gold_text, str,
780           Vector(screen->w-4*16, 40), LAYER_FOREGROUND1);
781     }
782 }
783
784 void
785 GameSession::drawresultscreen(void)
786 {
787   char str[80];
788
789   DrawingContext context;
790   currentsector->background->draw(context);  
791
792   context.draw_text_center(blue_text, _("Result:"), Vector(0, 200),
793       LAYER_FOREGROUND1);
794
795   sprintf(str, _("SCORE: %d"), player_status.score);
796   context.draw_text_center(gold_text, str, Vector(0, 224), LAYER_FOREGROUND1);
797
798   sprintf(str, _("COINS: %d"), player_status.distros);
799   context.draw_text_center(gold_text, str, Vector(0, 256), LAYER_FOREGROUND1);
800
801   context.do_drawing();
802   
803   SDL_Event event;
804   wait_for_event(event,2000,5000,true);
805 }
806
807 std::string slotinfo(int slot)
808 {
809   char tmp[1024];
810   char slotfile[1024];
811   std::string title;
812   sprintf(slotfile,"%s/slot%d.stsg",st_save_dir,slot);
813
814   lisp_object_t* savegame = lisp_read_from_file(slotfile);
815   if (savegame)
816     {
817       LispReader reader(lisp_cdr(savegame));
818       reader.read_string("title", title);
819       lisp_free(savegame);
820     }
821
822   if (access(slotfile, F_OK) == 0)
823     {
824       if (!title.empty())
825         snprintf(tmp,1024,"Slot %d - %s",slot, title.c_str());
826       else
827         snprintf(tmp, 1024,_("Slot %d - Savegame"),slot);
828     }
829   else
830     sprintf(tmp,_("Slot %d - Free"),slot);
831
832   return tmp;
833 }
834
835 bool process_load_game_menu()
836 {
837   int slot = load_game_menu->check();
838
839   if(slot != -1 && load_game_menu->get_item_by_id(slot).kind == MN_ACTION)
840     {
841       char slotfile[1024];
842       snprintf(slotfile, 1024, "%s/slot%d.stsg", st_save_dir, slot);
843
844       if (access(slotfile, F_OK) != 0)
845         {
846           draw_intro();
847         }
848
849       // shrink_fade(Point((screen->w/2),(screen->h/2)), 1000);
850       fadeout(256);
851
852       DrawingContext context;
853       context.draw_text_center(white_text, "Loading...",
854                                Vector(0, screen->h/2), LAYER_FOREGROUND1);
855       context.do_drawing();
856
857       WorldMapNS::WorldMap worldmap;
858
859       // Load the game or at least set the savegame_file variable
860       worldmap.loadgame(slotfile);
861
862       worldmap.display();
863
864       Menu::set_current(main_menu);
865
866       Ticks::pause_stop();
867       return true;
868     }
869   else
870     {
871       return false;
872     }
873 }