6aae47e09f26f9f54bd0621823851cdce2d00bed
[supertux.git] / src / game_session.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 #include <config.h>
22
23 #include <iostream>
24 #include <sstream>
25 #include <cassert>
26 #include <cstdio>
27 #include <cstdlib>
28 #include <cmath>
29 #include <cstring>
30 #include <cerrno>
31 #include <unistd.h>
32 #include <ctime>
33 #include <stdexcept>
34
35 #include <SDL.h>
36
37 #ifndef WIN32
38 #include <sys/types.h>
39 #include <ctype.h>
40 #endif
41
42 #include "app/globals.h"
43 #include "game_session.h"
44 #include "video/screen.h"
45 #include "app/setup.h"
46 #include "gui/menu.h"
47 #include "sector.h"
48 #include "level.h"
49 #include "tile.h"
50 #include "player_status.h"
51 #include "object/particlesystem.h"
52 #include "object/background.h"
53 #include "object/tilemap.h"
54 #include "object/camera.h"
55 #include "object/player.h"
56 #include "lisp/lisp.h"
57 #include "lisp/parser.h"
58 #include "resources.h"
59 #include "app/gettext.h"
60 #include "worldmap.h"
61 #include "misc.h"
62 #include "statistics.h"
63 #include "timer.h"
64 #include "object/fireworks.h"
65 #include "textscroller.h"
66 #include "control/codecontroller.h"
67 #include "control/joystickkeyboardcontroller.h"
68 #include "main.h"
69
70 GameSession* GameSession::current_ = 0;
71
72 GameSession::GameSession(const std::string& levelfile_, GameSessionMode mode,
73     Statistics* statistics)
74   : level(0), currentsector(0), mode(mode),
75     end_sequence(NO_ENDSEQUENCE), end_sequence_controller(0),
76     levelfile(levelfile_), best_level_statistics(statistics)
77 {
78   current_ = this;
79   
80   game_pause = false;
81   fps_fps = 0;
82
83   context = new DrawingContext();
84
85   restart_level();
86 }
87
88 void
89 GameSession::restart_level()
90 {
91   game_pause   = false;
92   exit_status  = ES_NONE;
93   end_sequence = NO_ENDSEQUENCE;
94
95   main_controller->reset();
96   last_keys.clear();
97
98   delete level;
99   currentsector = 0;
100
101   level = new Level;
102   level->load(levelfile);
103
104   global_stats.reset();
105   global_stats.set_total_points(COINS_COLLECTED_STAT, level->get_total_coins());
106   global_stats.set_total_points(BADGUYS_KILLED_STAT, level->get_total_badguys());
107   global_stats.set_total_points(TIME_NEEDED_STAT, level->timelimit);
108
109   if(reset_sector != "") {
110     currentsector = level->get_sector(reset_sector);
111     if(!currentsector) {
112       std::stringstream msg;
113       msg << "Couldn't find sector '" << reset_sector << "' for resetting tux.";
114       throw std::runtime_error(msg.str());
115     }
116     currentsector->activate(reset_pos);
117   } else {
118     currentsector = level->get_sector("main");
119     if(!currentsector)
120       throw std::runtime_error("Couldn't find main sector");
121     currentsector->activate("main");
122   }
123   
124   if(mode == ST_GL_PLAY || mode == ST_GL_LOAD_LEVEL_FILE)
125     levelintro();
126
127   start_timers();
128   currentsector->play_music(LEVEL_MUSIC);
129 }
130
131 GameSession::~GameSession()
132 {
133   delete end_sequence_controller;
134   delete level;
135   delete context;
136 }
137
138 void
139 GameSession::levelintro()
140 {
141   sound_manager->halt_music();
142   
143   char str[60];
144
145   DrawingContext context;
146   for(Sector::GameObjects::iterator i = currentsector->gameobjects.begin();
147       i != currentsector->gameobjects.end(); ++i) {
148     Background* background = dynamic_cast<Background*> (*i);
149     if(background) {
150       background->draw(context);
151     }
152   }
153
154 //  context.draw_text(gold_text, level->get_name(), Vector(SCREEN_WIDTH/2, 160),
155 //      CENTER_ALLIGN, LAYER_FOREGROUND1);
156   context.draw_center_text(gold_text, level->get_name(), Vector(0, 160),
157       LAYER_FOREGROUND1);
158
159   sprintf(str, "TUX x %d", player_status.lives);
160   context.draw_text(white_text, str, Vector(SCREEN_WIDTH/2, 210),
161       CENTER_ALLIGN, LAYER_FOREGROUND1);
162
163   if((level->get_author().size()) && (level->get_author() != "SuperTux Team"))
164     //TODO make author check case/blank-insensitive
165     context.draw_text(white_small_text,
166       std::string(_("contributed by ")) + level->get_author(), 
167       Vector(SCREEN_WIDTH/2, 350), CENTER_ALLIGN, LAYER_FOREGROUND1);
168
169
170   if(best_level_statistics != NULL)
171     best_level_statistics->draw_message_info(context, _("Best Level Statistics"));
172
173   context.do_drawing();
174
175   SDL_Event event;
176   wait_for_event(event,1000,3000,true);
177 }
178
179 /* Reset Timers */
180 void
181 GameSession::start_timers()
182 {
183   time_left.start(level->timelimit);
184   Ticks::pause_init();
185 }
186
187 void
188 GameSession::on_escape_press()
189 {
190   if(currentsector->player->is_dying() || end_sequence != NO_ENDSEQUENCE)
191     return;   // don't let the player open the menu, when he is dying
192   
193   if(mode == ST_GL_TEST) {
194     exit_status = ES_LEVEL_ABORT;
195   } else if (!Menu::current()) {
196     Menu::set_current(game_menu);
197     game_menu->set_active_item(MNID_CONTINUE);
198     Ticks::pause_start();
199     game_pause = true;
200   } else {
201     Ticks::pause_stop();
202     game_pause = false;
203   }
204 }
205
206 void
207 GameSession::process_events()
208 {
209   Player& tux = *currentsector->player;
210   main_controller->update();
211
212   // end of pause mode?
213   if(!Menu::current() && game_pause) {
214     game_pause = false;
215     Ticks::pause_stop();
216   }
217
218   if (end_sequence != NO_ENDSEQUENCE) {
219     if(end_sequence_controller == 0) {
220       end_sequence_controller = new CodeController();
221       tux.set_controller(end_sequence_controller);
222     }
223
224     end_sequence_controller->press(Controller::RIGHT);
225     
226     if (int(last_x_pos) == int(tux.get_pos().x))
227       end_sequence_controller->press(Controller::JUMP);    
228     last_x_pos = tux.get_pos().x;
229   }
230
231   main_controller->update();
232   SDL_Event event;
233   while (SDL_PollEvent(&event)) {
234     /* Check for menu-events, if the menu is shown */
235     if (Menu::current())
236       Menu::current()->event(event);
237     main_controller->process_event(event);
238     if(event.type == SDL_QUIT)
239       throw std::runtime_error("Received window close");  
240   }
241 }
242
243 void
244 GameSession::try_cheats()
245 {
246   Player& tux = *currentsector->player;
247   
248   // Cheating words (the goal of this is really for debugging,
249   // but could be used for some cheating, nothing wrong with that)
250   if(main_controller->check_cheatcode("grow")) {
251     tux.set_bonus(GROWUP_BONUS, false);
252     last_keys.clear();
253   }
254   if(main_controller->check_cheatcode("fire")) {
255     tux.set_bonus(FIRE_BONUS, false);
256     last_keys.clear();
257   }
258   if(main_controller->check_cheatcode("ice")) {
259     tux.set_bonus(ICE_BONUS, false);
260     last_keys.clear();
261   }
262   if(main_controller->check_cheatcode("lifeup")) {
263     player_status.lives++;
264     last_keys.clear();
265   }
266   if(main_controller->check_cheatcode("lifedown")) {
267     player_status.lives--;
268     last_keys.clear();
269   }
270   if(main_controller->check_cheatcode("grease")) {
271     tux.physic.set_velocity_x(tux.physic.get_velocity_x()*3);
272     last_keys.clear();
273   }
274   if(main_controller->check_cheatcode("invincible")) {
275     // be invincle for the rest of the level
276     tux.invincible_timer.start(10000);
277     last_keys.clear();
278   }
279   if(main_controller->check_cheatcode("shrink")) {
280     // remove powerups
281     tux.kill(tux.SHRINK);
282     last_keys.clear();
283   }
284   if(main_controller->check_cheatcode("kill")) {
285     // kill Tux, but without losing a life
286     player_status.lives++;
287     tux.kill(tux.KILL);
288     last_keys.clear();
289   }
290 #if 0
291   if(main_controller->check_cheatcode("grid")) {
292     // toggle debug grid
293     debug_grid = !debug_grid;
294     last_keys.clear();
295   }
296 #endif
297   if(main_controller->check_cheatcode("hover")) {
298     // toggle hover ability on/off
299     tux.enable_hover = !tux.enable_hover;
300     last_keys.clear();
301   }
302   if(main_controller->check_cheatcode("gotoend")) {
303     // goes to the end of the level
304     tux.move(Vector(
305           (currentsector->solids->get_width()*32) - (SCREEN_WIDTH*2), 0));
306     currentsector->camera->reset(
307         Vector(tux.get_pos().x, tux.get_pos().y));
308     last_keys.clear();
309   }
310   if(main_controller->check_cheatcode("finish")) {
311     // finish current sector
312     exit_status = ES_LEVEL_FINISHED;
313     // don't add points to stats though...
314   }
315   // temporary to help player's choosing a flapping
316   if(main_controller->check_cheatcode("marek")) {
317     tux.flapping_mode = Player::MAREK_FLAP;
318     last_keys.clear();
319   }
320   if(main_controller->check_cheatcode("ricardo")) {
321     tux.flapping_mode = Player::RICARDO_FLAP;
322     last_keys.clear();
323   }
324   if(main_controller->check_cheatcode("ryan")) {
325     tux.flapping_mode = Player::RYAN_FLAP;
326     last_keys.clear();
327   }
328 }
329
330 void
331 GameSession::check_end_conditions()
332 {
333   Player* tux = currentsector->player;
334
335   /* End of level? */
336   if(end_sequence && endsequence_timer.check()) {
337     exit_status = ES_LEVEL_FINISHED;
338     return;
339   } else if (!end_sequence && tux->is_dead()) {
340     if (player_status.lives < 0) { // No more lives!?
341       exit_status = ES_GAME_OVER;
342     } else { // Still has lives, so reset Tux to the levelstart
343       restart_level();
344     }
345     
346     return;
347   }
348 }
349
350 void
351 GameSession::action(float elapsed_time)
352 {
353   // handle controller
354   if(main_controller->pressed(Controller::PAUSE_MENU))
355     on_escape_press();
356   
357   // advance timers
358   if(!currentsector->player->growing_timer.started()) {
359     // Update Tux and the World
360     currentsector->action(elapsed_time);
361   }
362
363   // respawning in new sector?
364   if(newsector != "" && newspawnpoint != "") {
365     Sector* sector = level->get_sector(newsector);
366     if(sector == 0) {
367       std::cerr << "Sector '" << newsector << "' not found.\n";
368     }
369     sector->activate(newspawnpoint);
370     sector->play_music(LEVEL_MUSIC);
371     currentsector = sector;
372     newsector = "";
373     newspawnpoint = "";
374   }
375 }
376
377 void 
378 GameSession::draw()
379 {
380   currentsector->draw(*context);
381   drawstatus(*context);
382
383   if(game_pause)
384     draw_pause();
385
386   if(Menu::current()) {
387     Menu::current()->draw(*context);
388     mouse_cursor->draw(*context);
389   }
390
391   context->do_drawing();
392 }
393
394 void
395 GameSession::draw_pause()
396 {
397   int x = SCREEN_HEIGHT / 20;
398   for(int i = 0; i < x; ++i) {
399     context->draw_filled_rect(
400         Vector(i % 2 ? (pause_menu_frame * i)%SCREEN_WIDTH :
401           -((pause_menu_frame * i)%SCREEN_WIDTH)
402           ,(i*20+pause_menu_frame)%SCREEN_HEIGHT),
403         Vector(SCREEN_WIDTH,10),
404         Color(20,20,20, rand() % 20 + 1), LAYER_FOREGROUND1+1);
405   }
406
407   context->draw_filled_rect(
408       Vector(0,0), Vector(SCREEN_WIDTH, SCREEN_HEIGHT),
409       Color(rand() % 50, rand() % 50, rand() % 50, 128), LAYER_FOREGROUND1);
410 #if 0
411   context->draw_text(blue_text, _("PAUSE - Press 'P' To Play"),
412       Vector(SCREEN_WIDTH/2, 230), CENTER_ALLIGN, LAYER_FOREGROUND1+2);
413
414   const char* str1 = _("Playing: ");
415   const char* str2 = level->get_name().c_str();
416
417   context->draw_text(blue_text, str1,
418       Vector((SCREEN_WIDTH - (blue_text->get_text_width(str1) + white_text->get_text_width(str2)))/2, 340),
419       LEFT_ALLIGN, LAYER_FOREGROUND1+2);
420   context->draw_text(white_text, str2,
421       Vector(((SCREEN_WIDTH - (blue_text->get_text_width(str1) + white_text->get_text_width(str2)))/2)+blue_text->get_text_width(str1), 340),
422       LEFT_ALLIGN, LAYER_FOREGROUND1+2);
423 #endif
424 }
425   
426 void
427 GameSession::process_menu()
428 {
429   Menu* menu = Menu::current();
430   if(menu) {
431     menu->action();
432
433     if(menu == game_menu) {
434       switch (game_menu->check()) {
435         case MNID_CONTINUE:
436           Menu::set_current(0);
437           break;
438         case MNID_ABORTLEVEL:
439           Menu::set_current(0);
440           exit_status = ES_LEVEL_ABORT;
441           break;
442       }
443     } else if(menu == options_menu) {
444       process_options_menu();
445     } else if(menu == load_game_menu ) {
446       process_load_game_menu();
447     }
448   }
449 }
450
451
452 GameSession::ExitStatus
453 GameSession::run()
454 {
455   Menu::set_current(0);
456   current_ = this;
457   
458   int fps_cnt = 0;
459   double fps_nextframe_ticks; // fps regulating code
460
461   // Eat unneeded events
462   SDL_Event event;
463   while(SDL_PollEvent(&event))
464   {}
465
466   draw();
467
468   Uint32 lastticks = SDL_GetTicks();
469   fps_ticks = SDL_GetTicks();
470   fps_nextframe_ticks = SDL_GetTicks(); // fps regulating code
471
472   while (exit_status == ES_NONE) {
473     Uint32 ticks = SDL_GetTicks();
474     float elapsed_time = float(ticks - lastticks) / 1000.;
475     if(!game_pause)
476       global_time += elapsed_time;
477     lastticks = ticks;
478
479     // 40fps is minimum
480     if(elapsed_time > 0.025){
481       elapsed_time = 0.025; 
482     }
483             
484     // fps regualting code  
485     const double wantedFps= 60.0; // set to 60 by now
486     while (fps_nextframe_ticks > SDL_GetTicks()){
487             /* just wait */
488             // If we really have to wait long, then do an imprecise SDL_Delay()
489             Uint32 ticks = SDL_GetTicks();
490             if (fps_nextframe_ticks - ticks > 15) {
491                 SDL_Delay((Uint32) (fps_nextframe_ticks - ticks));
492             } 
493     }
494     float diff = SDL_GetTicks() - fps_nextframe_ticks;
495     if (diff > 5.0)
496         fps_nextframe_ticks = SDL_GetTicks() + (1000.0 / wantedFps); // sets the ticks that must have elapsed
497     else
498         fps_nextframe_ticks += 1000.0 / wantedFps; // sets the ticks that must have elapsed
499                                                // in order for the next frame to start.
500
501     process_events();
502     process_menu();
503
504     // Update the world state and all objects in the world
505     // Do that with a constante time-delta so that the game will run
506     // determistic and not different on different machines
507     if(!game_pause && !Menu::current())
508     {
509       // Update the world
510       check_end_conditions();
511       if (end_sequence == ENDSEQUENCE_RUNNING)
512         action(elapsed_time/2);
513       else if(end_sequence == NO_ENDSEQUENCE)
514         action(elapsed_time);
515     }
516     else
517     {
518       ++pause_menu_frame;
519       SDL_Delay(50);
520     }
521
522     draw();
523
524     /* Time stops in pause mode */
525     if(game_pause || Menu::current())
526     {
527       continue;
528     }
529
530     //frame_rate.update();
531     
532     /* Handle time: */
533     if (time_left.check() && !end_sequence)
534       currentsector->player->kill(Player::KILL);
535     
536     /* Handle music: */
537     if (currentsector->player->invincible_timer.started() && !end_sequence)
538     {
539       currentsector->play_music(HERRING_MUSIC);
540     }
541     /* or just normal music? */
542     else if(currentsector->get_music_type() != LEVEL_MUSIC && !end_sequence)
543     {
544       currentsector->play_music(LEVEL_MUSIC);
545     }
546
547     /* Calculate frames per second */
548     if(config->show_fps)
549     {
550       ++fps_cnt;
551       
552       if(SDL_GetTicks() - fps_ticks >= 500)
553       {
554         fps_fps = (float) fps_cnt / .5;
555         fps_cnt = 0;
556         fps_ticks = SDL_GetTicks();
557       }
558     }
559   }
560  
561   // just in case
562   main_controller->reset();
563   return exit_status;
564 }
565
566 void
567 GameSession::respawn(const std::string& sector, const std::string& spawnpoint)
568 {
569   newsector = sector;
570   newspawnpoint = spawnpoint;
571 }
572
573 void
574 GameSession::set_reset_point(const std::string& sector, const Vector& pos)
575 {
576   reset_sector = sector;
577   reset_pos = pos;
578 }
579
580 void
581 GameSession::display_info_box(const std::string& text)
582 {
583   InfoBox* box = new InfoBox(text);
584
585   bool running = true;
586   while(running)  {
587
588     main_controller->update();
589     SDL_Event event;
590     while (SDL_PollEvent(&event)) {
591       main_controller->process_event(event);
592       if(event.type == SDL_QUIT)
593         throw std::runtime_error("Received window close event");
594     }
595
596     if(main_controller->pressed(Controller::JUMP)
597         || main_controller->pressed(Controller::ACTION)
598         || main_controller->pressed(Controller::PAUSE_MENU)
599         || main_controller->pressed(Controller::MENU_SELECT))
600       running = false;
601     box->draw(*context);
602     draw();
603   }
604
605   delete box;
606 }
607
608 void
609 GameSession::start_sequence(const std::string& sequencename)
610 {
611   if(sequencename == "endsequence" || sequencename == "fireworks") {
612     if(end_sequence)
613       return;
614     
615     end_sequence = ENDSEQUENCE_RUNNING;
616     endsequence_timer.start(7.0); // 7 seconds until we finish the map
617     last_x_pos = -1;
618     sound_manager->play_music(level_end_song, 0);
619     currentsector->player->invincible_timer.start(7.0);
620
621     // add left time to stats
622     global_stats.set_points(TIME_NEEDED_STAT,
623         int(time_left.get_period() - time_left.get_timeleft()));
624
625     if(sequencename == "fireworks") {
626       currentsector->add_object(new Fireworks());
627     }
628   } else if(sequencename == "stoptux") {
629     end_sequence =  ENDSEQUENCE_WAITING;
630   } else {
631     std::cout << "Unknown sequence '" << sequencename << "'.\n";
632   }
633 }
634
635 /* (Status): */
636 void
637 GameSession::drawstatus(DrawingContext& context)
638 {
639   char str[60];
640   
641   snprintf(str, 60, " %d", global_stats.get_points(SCORE_STAT));
642   context.draw_text(white_text, _("SCORE"), Vector(0, 0), LEFT_ALLIGN, LAYER_FOREGROUND1);
643   context.draw_text(gold_text, str, Vector(96, 0), LEFT_ALLIGN, LAYER_FOREGROUND1);
644
645   if(mode == ST_GL_TEST) {
646     context.draw_text(white_text, _("Press ESC To Return"), Vector(0,20),
647                       LEFT_ALLIGN, LAYER_FOREGROUND1);
648   }
649
650   if(time_left.get_timeleft() < 0) {
651     context.draw_text(white_text, _("TIME's UP"), Vector(SCREEN_WIDTH/2, 0),
652         CENTER_ALLIGN, LAYER_FOREGROUND1);
653   } else if (time_left.get_timeleft() > TIME_WARNING
654       || int(global_time * 2.5) % 2) {
655     sprintf(str, " %d", int(time_left.get_timeleft()));
656     context.draw_text(white_text, _("TIME"),
657         Vector(SCREEN_WIDTH/2, 0), CENTER_ALLIGN, LAYER_FOREGROUND1);
658     context.draw_text(gold_text, str,
659         Vector(SCREEN_WIDTH/2 + 4*16, 0), CENTER_ALLIGN, LAYER_FOREGROUND1);
660   }
661
662   sprintf(str, " %d", player_status.coins);
663   context.draw_text(white_text, _("COINS"),
664       Vector(SCREEN_WIDTH - white_text->get_text_width(_("COINS"))-white_text->get_text_width("   99"), 0),
665         LEFT_ALLIGN, LAYER_FOREGROUND1);
666   context.draw_text(gold_text, str,
667       Vector(SCREEN_WIDTH - gold_text->get_text_width(" 99"), 0),LEFT_ALLIGN, LAYER_FOREGROUND1);
668
669   if (player_status.lives >= 5)
670     {
671       sprintf(str, "%dx", player_status.lives);
672       float x = SCREEN_WIDTH - gold_text->get_text_width(str) - tux_life->w;
673       context.draw_text(gold_text, str, Vector(x, 20), LEFT_ALLIGN, LAYER_FOREGROUND1);
674       context.draw_surface(tux_life, Vector(SCREEN_WIDTH - 16, 20),
675           LAYER_FOREGROUND1);
676     }
677   else
678     {
679       for(int i= 0; i < player_status.lives; ++i)
680         context.draw_surface(tux_life, 
681             Vector(SCREEN_WIDTH - tux_life->w*4 +(tux_life->w*i), 20),
682             LAYER_FOREGROUND1);
683     }
684
685   context.draw_text(white_text, _("LIVES"),
686       Vector(SCREEN_WIDTH - white_text->get_text_width(_("LIVES")) - white_text->get_text_width("   99"), 20),
687       LEFT_ALLIGN, LAYER_FOREGROUND1);
688
689   if(config->show_fps) {
690     sprintf(str, "%2.1f", fps_fps);
691     context.draw_text(white_text, "FPS", 
692                       Vector(SCREEN_WIDTH -
693                              white_text->get_text_width("FPS     "), 40),
694                       LEFT_ALLIGN, LAYER_FOREGROUND1);
695     context.draw_text(gold_text, str,
696                       Vector(SCREEN_WIDTH-4*16, 40),
697                       LEFT_ALLIGN, LAYER_FOREGROUND1);
698   }
699 }
700
701 void
702 GameSession::drawresultscreen()
703 {
704   char str[80];
705
706   DrawingContext context;
707   for(Sector::GameObjects::iterator i = currentsector->gameobjects.begin();
708       i != currentsector->gameobjects.end(); ++i) {
709     Background* background = dynamic_cast<Background*> (*i);
710     if(background) {
711       background->draw(context);
712     }
713   }
714
715   context.draw_text(blue_text, _("Result:"), Vector(SCREEN_WIDTH/2, 200),
716       CENTER_ALLIGN, LAYER_FOREGROUND1);
717
718   sprintf(str, _("SCORE: %d"), global_stats.get_points(SCORE_STAT));
719   context.draw_text(gold_text, str, Vector(SCREEN_WIDTH/2, 224), CENTER_ALLIGN, LAYER_FOREGROUND1);
720
721   sprintf(str, _("COINS: %d"), player_status.coins);
722   context.draw_text(gold_text, str, Vector(SCREEN_WIDTH/2, 256), CENTER_ALLIGN, LAYER_FOREGROUND1);
723
724   context.do_drawing();
725   
726   SDL_Event event;
727   wait_for_event(event,2000,5000,true);
728 }
729
730 std::string slotinfo(int slot)
731 {
732   std::string tmp;
733   std::string slotfile;
734   std::string title;
735   std::stringstream stream;
736   stream << slot;
737   slotfile = user_dir + "/save/slot" + stream.str() + ".stsg";
738
739   try {
740     lisp::Parser parser;
741     std::auto_ptr<lisp::Lisp> root (parser.parse(slotfile));
742
743     const lisp::Lisp* savegame = root->get_lisp("supertux-savegame");
744     if(!savegame)
745       throw std::runtime_error("file is not a supertux-savegame.");
746
747     savegame->get("title", title);
748   } catch(std::exception& e) {
749     return std::string(_("Slot")) + " " + stream.str() + " - " +
750       std::string(_("Free"));
751   }
752
753   return std::string("Slot ") + stream.str() + " - " + title;
754 }
755
756 bool process_load_game_menu()
757 {
758   int slot = load_game_menu->check();
759
760   if(slot != -1 && load_game_menu->get_item_by_id(slot).kind == MN_ACTION)
761     {
762       std::stringstream stream;
763       stream << slot;
764       std::string slotfile = user_dir + "/save/slot" + stream.str() + ".stsg";
765
766       fadeout(256);
767       DrawingContext context;
768       context.draw_text(white_text, "Loading...",
769                         Vector(SCREEN_WIDTH/2, SCREEN_HEIGHT/2), CENTER_ALLIGN, LAYER_FOREGROUND1);
770       context.do_drawing();
771
772       WorldMapNS::WorldMap worldmap;
773
774       worldmap.set_map_filename("/levels/world1/worldmap.stwm");
775       // Load the game or at least set the savegame_file variable
776       worldmap.loadgame(slotfile);
777
778       worldmap.display();
779
780       Menu::set_current(main_menu);
781
782       Ticks::pause_stop();
783       return true;
784     }
785   else
786     {
787       return false;
788     }
789 }