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