7a124f51cad3fda8e8a54c15a13e10e94c55fbf7
[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 <fstream>
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 #include "game_session.hpp"
38 #include "msg.hpp"
39 #include "video/screen.hpp"
40 #include "audio/sound_manager.hpp"
41 #include "gui/menu.hpp"
42 #include "sector.hpp"
43 #include "level.hpp"
44 #include "tile.hpp"
45 #include "player_status.hpp"
46 #include "object/particlesystem.hpp"
47 #include "object/background.hpp"
48 #include "object/gradient.hpp"
49 #include "object/tilemap.hpp"
50 #include "object/camera.hpp"
51 #include "object/player.hpp"
52 #include "object/level_time.hpp"
53 #include "lisp/lisp.hpp"
54 #include "lisp/parser.hpp"
55 #include "resources.hpp"
56 #include "worldmap.hpp"
57 #include "misc.hpp"
58 #include "statistics.hpp"
59 #include "timer.hpp"
60 #include "object/fireworks.hpp"
61 #include "textscroller.hpp"
62 #include "control/codecontroller.hpp"
63 #include "control/joystickkeyboardcontroller.hpp"
64 #include "main.hpp"
65 #include "file_system.hpp"
66 #include "gameconfig.hpp"
67 #include "gettext.hpp"
68 #include "exceptions.hpp"
69 #include "flip_level_transformer.hpp"
70
71 // the engine will be run with a logical framerate of 64fps.
72 // We chose 64fps here because it is a power of 2, so 1/64 gives an "even"
73 // binary fraction...
74 static const float LOGICAL_FPS = 64.0;
75
76 GameSession* GameSession::current_ = 0;
77
78 GameSession::GameSession(const std::string& levelfile_, GameSessionMode mode,
79     Statistics* statistics)
80   : level(0), currentsector(0), mode(mode),
81     end_sequence(NO_ENDSEQUENCE), end_sequence_controller(0),
82     levelfile(levelfile_), best_level_statistics(statistics),
83     capture_demo_stream(0), playback_demo_stream(0), demo_controller(0)
84 {
85   current_ = this;
86   currentsector = 0;
87   
88   game_pause = false;
89   fps_fps = 0;
90
91   context = new DrawingContext();
92   console = new Console(context);
93   Console::registerCommandReceiver(this);
94
95   restart_level();
96 }
97
98 void
99 GameSession::restart_level()
100 {
101   game_pause   = false;
102   exit_status  = ES_NONE;
103   end_sequence = NO_ENDSEQUENCE;
104
105   main_controller->reset();
106
107   delete level;
108   currentsector = 0;
109
110   level = new Level;
111   level->load(levelfile);
112
113   global_stats.reset();
114   global_stats.set_total_points(COINS_COLLECTED_STAT, level->get_total_coins());
115   global_stats.set_total_points(BADGUYS_KILLED_STAT, level->get_total_badguys());
116   
117   // get time
118   int time = 0;
119   for(std::vector<Sector*>::iterator i = level->sectors.begin(); i != level->sectors.end(); ++i)
120   {
121     Sector* sec = *i;
122
123     for(std::vector<GameObject*>::iterator j = sec->gameobjects.begin();
124         j != sec->gameobjects.end(); ++j)
125     {
126       GameObject* obj = *j;
127       
128       LevelTime* lt = dynamic_cast<LevelTime*> (obj);
129       if(lt)
130         time += int(lt->get_level_time());
131     }
132   }
133   global_stats.set_total_points(TIME_NEEDED_STAT, (time == 0) ? -1 : time);
134
135   if(reset_sector != "") {
136     currentsector = level->get_sector(reset_sector);
137     if(!currentsector) {
138       std::stringstream msg;
139       msg << "Couldn't find sector '" << reset_sector << "' for resetting tux.";
140       throw std::runtime_error(msg.str());
141     }
142     currentsector->activate(reset_pos);
143   } else {
144     currentsector = level->get_sector("main");
145     if(!currentsector)
146       throw std::runtime_error("Couldn't find main sector");
147     currentsector->activate("main");
148   }
149   
150   if(mode == ST_GL_PLAY || mode == ST_GL_LOAD_LEVEL_FILE)
151     levelintro();
152
153   currentsector->play_music(LEVEL_MUSIC);
154
155   if(capture_file != "")
156     record_demo(capture_file);
157 }
158
159 GameSession::~GameSession()
160 {
161   delete capture_demo_stream;
162   delete playback_demo_stream;
163   delete demo_controller;
164
165   delete end_sequence_controller;
166   delete level;
167   delete context;
168   Console::unregisterCommandReceiver(this);
169   delete console;
170
171   current_ = NULL;
172 }
173
174 void
175 GameSession::record_demo(const std::string& filename)
176 {
177   delete capture_demo_stream;
178   
179   capture_demo_stream = new std::ofstream(filename.c_str()); 
180   if(!capture_demo_stream->good()) {
181     std::stringstream msg;
182     msg << "Couldn't open demo file '" << filename << "' for writing.";
183     throw std::runtime_error(msg.str());
184   }
185   capture_file = filename;
186 }
187
188 void
189 GameSession::play_demo(const std::string& filename)
190 {
191   delete playback_demo_stream;
192   delete demo_controller;
193   
194   playback_demo_stream = new std::ifstream(filename.c_str());
195   if(!playback_demo_stream->good()) {
196     std::stringstream msg;
197     msg << "Couldn't open demo file '" << filename << "' for reading.";
198     throw std::runtime_error(msg.str());
199   }
200
201   Player& tux = *currentsector->player;
202   demo_controller = new CodeController();
203   tux.set_controller(demo_controller);
204 }
205
206 void
207 GameSession::levelintro()
208 {
209   char str[60];
210
211   sound_manager->stop_music();
212
213   DrawingContext context;
214   for(Sector::GameObjects::iterator i = currentsector->gameobjects.begin();
215       i != currentsector->gameobjects.end(); ++i) {
216     Background* background = dynamic_cast<Background*> (*i);
217     if(background) {
218       background->draw(context);
219     }
220     Gradient* gradient = dynamic_cast<Gradient*> (*i);
221     if(gradient) {
222       gradient->draw(context);
223     }
224   }
225
226 //  context.draw_text(gold_text, level->get_name(), Vector(SCREEN_WIDTH/2, 160),
227 //      CENTER_ALLIGN, LAYER_FOREGROUND1);
228   context.draw_center_text(gold_text, level->get_name(), Vector(0, 160),
229       LAYER_FOREGROUND1);
230
231   sprintf(str, "TUX x %d", player_status->lives);
232   context.draw_text(white_text, str, Vector(SCREEN_WIDTH/2, 210),
233       CENTER_ALLIGN, LAYER_FOREGROUND1);
234
235   if((level->get_author().size()) && (level->get_author() != "SuperTux Team"))
236     //TODO make author check case/blank-insensitive
237     context.draw_text(white_small_text,
238       std::string(_("contributed by ")) + level->get_author(), 
239       Vector(SCREEN_WIDTH/2, 350), CENTER_ALLIGN, LAYER_FOREGROUND1);
240
241
242   if(best_level_statistics != NULL)
243     best_level_statistics->draw_message_info(context, _("Best Level Statistics"));
244
245   console->draw();
246   context.do_drawing();
247
248   wait_for_event(1.0, 3.0);
249 }
250
251 void
252 GameSession::on_escape_press()
253 {
254   if(currentsector->player->is_dying() || end_sequence != NO_ENDSEQUENCE)
255     return;   // don't let the player open the menu, when he is dying
256   
257   if(mode == ST_GL_TEST) {
258     exit_status = ES_LEVEL_ABORT;
259   } else if (!Menu::current()) {
260     Menu::set_current(game_menu);
261     game_menu->set_active_item(MNID_CONTINUE);
262     game_pause = true;
263   } else {
264     game_pause = false;
265   }
266 }
267
268 void
269 GameSession::process_events()
270 {
271   Player& tux = *currentsector->player;
272   main_controller->update();
273
274   // end of pause mode?
275   if(!Menu::current() && game_pause) {
276     game_pause = false;
277   }
278
279   if (end_sequence != NO_ENDSEQUENCE) {
280     if(end_sequence_controller == 0) {
281       end_sequence_controller = new CodeController();
282       tux.set_controller(end_sequence_controller);
283     }
284
285     end_sequence_controller->press(Controller::RIGHT);
286     
287     if (int(last_x_pos) == int(tux.get_pos().x))
288       end_sequence_controller->press(Controller::JUMP);    
289     last_x_pos = tux.get_pos().x;
290   }
291
292   main_controller->update();
293   SDL_Event event;
294   while (SDL_PollEvent(&event)) {
295     /* Check for menu-events, if the menu is shown */
296     if (Menu::current())
297       Menu::current()->event(event);
298     main_controller->process_event(event);
299     if(event.type == SDL_QUIT)
300       throw graceful_shutdown();
301   }
302
303   // playback a demo?
304   if(playback_demo_stream != 0) {
305     demo_controller->update();
306     char left = false;
307     char right = false;
308     char up = false;
309     char down = false;
310     char jump = false;
311     char action = false;
312     playback_demo_stream->get(left);
313     playback_demo_stream->get(right);
314     playback_demo_stream->get(up);
315     playback_demo_stream->get(down);
316     playback_demo_stream->get(jump);
317     playback_demo_stream->get(action);
318     demo_controller->press(Controller::LEFT, left);
319     demo_controller->press(Controller::RIGHT, right);
320     demo_controller->press(Controller::UP, up);
321     demo_controller->press(Controller::DOWN, down);
322     demo_controller->press(Controller::JUMP, jump);
323     demo_controller->press(Controller::ACTION, action);
324   }
325
326   // save input for demo?
327   if(capture_demo_stream != 0) {
328     capture_demo_stream ->put(main_controller->hold(Controller::LEFT));
329     capture_demo_stream ->put(main_controller->hold(Controller::RIGHT));
330     capture_demo_stream ->put(main_controller->hold(Controller::UP));
331     capture_demo_stream ->put(main_controller->hold(Controller::DOWN));
332     capture_demo_stream ->put(main_controller->hold(Controller::JUMP));   
333     capture_demo_stream ->put(main_controller->hold(Controller::ACTION));
334   }
335 }
336
337 bool
338 GameSession::consoleCommand(std::string command)
339 {
340   if (command == "foo") {
341     msg_info("bar");
342     return true;
343   }
344
345   if (currentsector == 0) return false;
346   Player& tux = *currentsector->player;
347   
348   // Cheating words (the goal of this is really for debugging,
349   // but could be used for some cheating, nothing wrong with that)
350   if (command == "grow") {
351     tux.set_bonus(GROWUP_BONUS, false);
352     return true;
353   }
354   if (command == "fire") {
355     tux.set_bonus(FIRE_BONUS, false);
356     return true;
357   }
358   if (command == "ice") {
359     tux.set_bonus(ICE_BONUS, false);
360     return true;
361   }
362   if (command == "lifeup") {
363     player_status->lives++;
364     return true;
365   }
366   if (command == "lifedown") {
367     player_status->lives--;
368     return true;
369   }
370   if (command == "grease") {
371     tux.physic.set_velocity_x(tux.physic.get_velocity_x()*3);
372     return true;
373   }
374   if (command == "invincible") {
375     // be invincle for the rest of the level
376     tux.invincible_timer.start(10000);
377     return true;
378   }
379   if (command == "mortal") {
380     // give up invincibility
381     tux.invincible_timer.stop();
382     return true;
383   }
384   if (command == "shrink") {
385     // remove powerups
386     tux.kill(tux.SHRINK);
387     return true;
388   }
389   if (command == "kill") {
390     // kill Tux, but without losing a life
391     player_status->lives++;
392     tux.kill(tux.KILL);
393     return true;
394   }
395   if (command == "whereami") {
396     msg_info("You are at x " << tux.get_pos().x << ", y " << tux.get_pos().y);
397     return true;
398   }
399 #if 0
400   if(command == "grid")) {
401     // toggle debug grid
402     debug_grid = !debug_grid;
403     return true;
404   }
405 #endif
406   if (command == "gotoend") {
407     // goes to the end of the level
408     tux.move(Vector(
409           (currentsector->solids->get_width()*32) - (SCREEN_WIDTH*2), 0));
410     currentsector->camera->reset(
411         Vector(tux.get_pos().x, tux.get_pos().y));
412     return true;
413   }
414   if (command == "flip") {
415         FlipLevelTransformer flip_transformer;
416     flip_transformer.transform(GameSession::current()->get_current_level());
417     return true;
418   }
419   if (command == "finish") {
420     // finish current sector
421     exit_status = ES_LEVEL_FINISHED;
422     // don't add points to stats though...
423     return true;
424   }
425   if (command == "camera") {
426     msg_info("Camera is at " 
427               << Sector::current()->camera->get_translation().x << "," 
428               << Sector::current()->camera->get_translation().y);
429     return true;
430   }
431
432   return false;
433 }
434
435 void
436 GameSession::check_end_conditions()
437 {
438   Player* tux = currentsector->player;
439
440   /* End of level? */
441   if(end_sequence && endsequence_timer.check()) {
442     exit_status = ES_LEVEL_FINISHED;
443     
444     // add time spent to statistics
445     int tottime = 0, remtime = 0;
446     for(std::vector<Sector*>::iterator i = level->sectors.begin(); i != level->sectors.end(); ++i)
447     {
448       Sector* sec = *i;
449
450       for(std::vector<GameObject*>::iterator j = sec->gameobjects.begin();
451           j != sec->gameobjects.end(); ++j)
452       {
453         GameObject* obj = *j;
454
455         LevelTime* lt = dynamic_cast<LevelTime*> (obj);
456         if(lt)
457         {
458           tottime += int(lt->get_level_time());
459           remtime += int(lt->get_remaining_time());
460         }
461       }
462     }
463     global_stats.set_points(TIME_NEEDED_STAT, (tottime == 0 ? -1 : (tottime-remtime)));
464
465     return;
466   } else if (!end_sequence && tux->is_dead()) {
467     if (player_status->lives < 0) { // No more lives!?
468       exit_status = ES_GAME_OVER;
469     } else { // Still has lives, so reset Tux to the levelstart
470       restart_level();
471     }
472     
473     return;
474   }
475 }
476
477 void
478 GameSession::update(float elapsed_time)
479 {
480   // handle controller
481   if(main_controller->pressed(Controller::PAUSE_MENU))
482     on_escape_press();
483   
484   // advance timers
485   if(!currentsector->player->growing_timer.started()) {
486     // Update Tux and the World
487     currentsector->update(elapsed_time);
488   }
489
490   // respawning in new sector?
491   if(newsector != "" && newspawnpoint != "") {
492     Sector* sector = level->get_sector(newsector);
493     if(sector == 0) {
494       msg_warning("Sector '" << newsector << "' not found");
495     }
496     sector->activate(newspawnpoint);
497     sector->play_music(LEVEL_MUSIC);
498     currentsector = sector;
499     newsector = "";
500     newspawnpoint = "";
501   }
502
503   // update sounds
504   sound_manager->set_listener_position(currentsector->player->get_pos());
505 }
506
507 void 
508 GameSession::draw()
509 {
510   currentsector->draw(*context);
511   drawstatus(*context);
512
513   if(game_pause)
514     draw_pause();
515
516   if(Menu::current()) {
517     Menu::current()->draw(*context);
518   }
519
520   console->draw();
521   context->do_drawing();
522 }
523
524 void
525 GameSession::draw_pause()
526 {
527   context->draw_filled_rect(
528       Vector(0,0), Vector(SCREEN_WIDTH, SCREEN_HEIGHT),
529       Color(.2, .2, .2, .5), LAYER_FOREGROUND1);
530 }
531   
532 void
533 GameSession::process_menu()
534 {
535   Menu* menu = Menu::current();
536   if(menu) {
537     menu->update();
538
539     if(menu == game_menu) {
540       switch (game_menu->check()) {
541         case MNID_CONTINUE:
542           Menu::set_current(0);
543           break;
544         case MNID_ABORTLEVEL:
545           Menu::set_current(0);
546           exit_status = ES_LEVEL_ABORT;
547           break;
548       }
549     } else if(menu == options_menu) {
550       process_options_menu();
551     } else if(menu == load_game_menu ) {
552       process_load_game_menu();
553     }
554   }
555 }
556
557
558 GameSession::ExitStatus
559 GameSession::run()
560 {
561   Menu::set_current(0);
562   current_ = this;
563   
564   int fps_cnt = 0;
565
566   // Eat unneeded events
567   SDL_Event event;
568   while(SDL_PollEvent(&event))
569   {}
570
571   draw();
572
573   Uint32 fps_ticks = SDL_GetTicks();
574   Uint32 fps_nextframe_ticks = SDL_GetTicks();
575   Uint32 ticks;
576   bool skipdraw = false;
577
578   while (exit_status == ES_NONE) {
579     // we run in a logical framerate so elapsed time is a constant
580     static const float elapsed_time = 1.0 / LOGICAL_FPS;
581     // old code... float elapsed_time = float(ticks - lastticks) / 1000.;
582     if(!game_pause)
583       game_time += elapsed_time;
584
585     // regulate fps
586     ticks = SDL_GetTicks();
587     if(ticks > fps_nextframe_ticks) {
588       if(skipdraw == true) {
589         // already skipped last frame? we have to slow down the game then...
590         skipdraw = false;
591         fps_nextframe_ticks -= (Uint32) (1000.0 / LOGICAL_FPS);
592       } else {
593         // don't draw all frames when we're getting too slow
594         skipdraw = true;
595       }
596     } else {
597       skipdraw = false;
598       while(fps_nextframe_ticks > ticks) {
599         /* just wait */
600         // If we really have to wait long, then do an imprecise SDL_Delay()
601         Uint32 diff = fps_nextframe_ticks - ticks;
602         if(diff > 15) {
603           SDL_Delay(diff - 10);
604         } 
605         ticks = SDL_GetTicks();
606       }
607     }
608     fps_nextframe_ticks = ticks + (Uint32) (1000.0 / LOGICAL_FPS);
609
610 #if 0
611     float diff = SDL_GetTicks() - fps_nextframe_ticks;
612     if (diff > 5.0) {
613          // sets the ticks that must have elapsed
614         fps_nextframe_ticks = SDL_GetTicks() + (1000.0 / LOGICAL_FPS);
615     } else {
616         // sets the ticks that must have elapsed
617         // in order for the next frame to start.
618         fps_nextframe_ticks += 1000.0 / LOGICAL_FPS;
619     }
620 #endif
621
622     process_events();
623     process_menu();
624
625     // Update the world state and all objects in the world
626     // Do that with a constante time-delta so that the game will run
627     // determistic and not different on different machines
628     if(!game_pause && !Menu::current())
629     {
630       // Update the world
631       check_end_conditions();
632       if (end_sequence == ENDSEQUENCE_RUNNING)
633         update(elapsed_time/2);
634       else if(end_sequence == NO_ENDSEQUENCE)
635         update(elapsed_time);
636     }
637     else
638     {
639       ++pause_menu_frame;
640     }
641
642     if(!skipdraw)
643       draw();
644
645     // update sounds
646     sound_manager->update();
647
648     /* Time stops in pause mode */
649     if(game_pause || Menu::current())
650     {
651       continue;
652     }
653
654     //frame_rate.update();
655     
656     /* Handle music: */
657     if (currentsector->player->invincible_timer.started() && 
658             currentsector->player->invincible_timer.get_timeleft() 
659             > TUX_INVINCIBLE_TIME_WARNING && !end_sequence)
660     {
661       currentsector->play_music(HERRING_MUSIC);
662     }
663     /* or just normal music? */
664     else if(currentsector->get_music_type() != LEVEL_MUSIC && !end_sequence)
665     {
666       currentsector->play_music(LEVEL_MUSIC);
667     }
668
669     /* Calculate frames per second */
670     if(config->show_fps)
671     {
672       ++fps_cnt;
673       
674       if(SDL_GetTicks() - fps_ticks >= 500)
675       {
676         fps_fps = (float) fps_cnt / .5;
677         fps_cnt = 0;
678         fps_ticks = SDL_GetTicks();
679       }
680     }
681   }
682  
683   // just in case
684   currentsector = 0;
685   main_controller->reset();
686   return exit_status;
687 }
688
689 void
690 GameSession::finish(bool win)
691 {
692   if(win)
693     exit_status = ES_LEVEL_FINISHED;
694   else
695     exit_status = ES_LEVEL_ABORT;
696 }
697
698 void
699 GameSession::respawn(const std::string& sector, const std::string& spawnpoint)
700 {
701   newsector = sector;
702   newspawnpoint = spawnpoint;
703 }
704
705 void
706 GameSession::set_reset_point(const std::string& sector, const Vector& pos)
707 {
708   reset_sector = sector;
709   reset_pos = pos;
710 }
711
712 std::string
713 GameSession::get_working_directory()
714 {
715   return FileSystem::dirname(levelfile);
716 }
717
718 void
719 GameSession::display_info_box(const std::string& text)
720 {
721   InfoBox* box = new InfoBox(text);
722
723   bool running = true;
724   while(running)  {
725
726     main_controller->update();
727     SDL_Event event;
728     while (SDL_PollEvent(&event)) {
729       main_controller->process_event(event);
730       if(event.type == SDL_QUIT)
731         throw graceful_shutdown();
732     }
733
734     if(main_controller->pressed(Controller::JUMP)
735         || main_controller->pressed(Controller::ACTION)
736         || main_controller->pressed(Controller::PAUSE_MENU)
737         || main_controller->pressed(Controller::MENU_SELECT))
738       running = false;
739     else if(main_controller->pressed(Controller::DOWN))
740       box->scrolldown();
741     else if(main_controller->pressed(Controller::UP))
742       box->scrollup();
743     box->draw(*context);
744     draw();
745     sound_manager->update();
746   }
747
748   delete box;
749 }
750
751 void
752 GameSession::start_sequence(const std::string& sequencename)
753 {
754   if(sequencename == "endsequence" || sequencename == "fireworks") {
755     if(end_sequence)
756       return;
757
758     end_sequence = ENDSEQUENCE_RUNNING;
759     endsequence_timer.start(7.3);
760     last_x_pos = -1;
761     sound_manager->play_music("music/leveldone.ogg", false);
762     currentsector->player->invincible_timer.start(7.3);
763
764     // Stop all clocks.
765     for(std::vector<GameObject*>::iterator i = currentsector->gameobjects.begin();
766         i != currentsector->gameobjects.end(); ++i)
767     {
768       GameObject* obj = *i;
769
770       LevelTime* lt = dynamic_cast<LevelTime*> (obj);
771       if(lt)
772         lt->stop();
773     }
774
775     if(sequencename == "fireworks") {
776       currentsector->add_object(new Fireworks());
777     }
778   } else if(sequencename == "stoptux") {
779     if(!end_sequence) {
780       msg_warning("Final target reached without "
781         << "an active end sequence");
782       this->start_sequence("endsequence");
783     }
784     end_sequence =  ENDSEQUENCE_WAITING;
785   } else {
786     msg_warning("Unknown sequence '" << sequencename << "'");
787   }
788 }
789
790 /* (Status): */
791 void
792 GameSession::drawstatus(DrawingContext& context)
793 {
794   player_status->draw(context);
795
796   if(config->show_fps) {
797     char str[60];
798     snprintf(str, sizeof(str), "%3.1f", fps_fps);
799     context.draw_text(white_text, "FPS", 
800                       Vector(SCREEN_WIDTH -
801                              white_text->get_text_width("FPS     ") - BORDER_X, BORDER_Y + 40),
802                       LEFT_ALLIGN, LAYER_FOREGROUND1);
803     context.draw_text(gold_text, str,
804                       Vector(SCREEN_WIDTH-4*16 - BORDER_X, BORDER_Y + 40),
805                       LEFT_ALLIGN, LAYER_FOREGROUND1);
806   }
807 }
808
809 void
810 GameSession::drawresultscreen()
811 {
812   char str[80];
813
814   DrawingContext context;
815   for(Sector::GameObjects::iterator i = currentsector->gameobjects.begin();
816       i != currentsector->gameobjects.end(); ++i) {
817     Background* background = dynamic_cast<Background*> (*i);
818     if(background) {
819       background->draw(context);
820     }
821   }
822
823   context.draw_text(blue_text, _("Result:"), Vector(SCREEN_WIDTH/2, 200),
824       CENTER_ALLIGN, LAYER_FOREGROUND1);
825
826 //  sprintf(str, _("SCORE: %d"), global_stats.get_points(SCORE_STAT));
827 //  context.draw_text(gold_text, str, Vector(SCREEN_WIDTH/2, 224), CENTER_ALLIGN, LAYER_FOREGROUND1);
828
829   // y == 256 before removal of score
830   sprintf(str, _("COINS: %d"), player_status->coins);
831   context.draw_text(gold_text, str, Vector(SCREEN_WIDTH/2, 224), CENTER_ALLIGN, LAYER_FOREGROUND1);
832
833   console->draw();
834   context.do_drawing();
835   
836   wait_for_event(2.0, 5.0);
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 = "save/slot" + stream.str() + ".stsg";
847
848   try {
849     lisp::Parser parser;
850     std::auto_ptr<lisp::Lisp> root (parser.parse(slotfile));
851
852     const lisp::Lisp* savegame = root->get_lisp("supertux-savegame");
853     if(!savegame)
854       throw std::runtime_error("file is not a supertux-savegame.");
855
856     savegame->get("title", title);
857   } catch(std::exception& e) {
858     return std::string(_("Slot")) + " " + stream.str() + " - " +
859       std::string(_("Free"));
860   }
861
862   return std::string("Slot ") + stream.str() + " - " + title;
863 }
864
865 bool process_load_game_menu()
866 {
867   int slot = load_game_menu->check();
868
869   if(slot == -1)
870     return false;
871   
872   if(load_game_menu->get_item_by_id(slot).kind != MN_ACTION)
873     return false;
874   
875   std::stringstream stream;
876   stream << slot;
877   std::string slotfile = "save/slot" + stream.str() + ".stsg";
878
879   sound_manager->stop_music();
880   fadeout(256);
881   DrawingContext context;
882   context.draw_text(white_text, "Loading...",
883                     Vector(SCREEN_WIDTH/2, SCREEN_HEIGHT/2),
884                     CENTER_ALLIGN, LAYER_FOREGROUND1);
885   context.do_drawing();
886
887   WorldMapNS::WorldMap worldmap;
888
889   worldmap.set_map_filename("/levels/world1/worldmap.stwm");
890   // Load the game or at least set the savegame_file variable
891   worldmap.loadgame(slotfile);
892
893   worldmap.display();
894
895   Menu::set_current(main_menu);
896
897   return true;
898 }