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