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