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