hiding the time while end_sequence wasn't nice
[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 <assert.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <math.h>
27 #include <string.h>
28 #include <errno.h>
29 #include <unistd.h>
30 #include <math.h>
31 #include <time.h>
32 #include <SDL.h>
33
34 #ifndef WIN32
35 #include <sys/types.h>
36 #include <ctype.h>
37 #endif
38
39 #include "defines.h"
40 #include "globals.h"
41 #include "gameloop.h"
42 #include "screen.h"
43 #include "setup.h"
44 #include "high_scores.h"
45 #include "menu.h"
46 #include "badguy.h"
47 #include "world.h"
48 #include "special.h"
49 #include "player.h"
50 #include "level.h"
51 #include "scene.h"
52 #include "collision.h"
53 #include "tile.h"
54 #include "particlesystem.h"
55 #include "resources.h"
56 #include "music_manager.h"
57
58 GameSession* GameSession::current_ = 0;
59
60 GameSession::GameSession(const std::string& subset_, int levelnb_, int mode)
61   : world(0), st_gl_mode(mode), levelnb(levelnb_), end_sequence(NO_ENDSEQUENCE),
62     subset(subset_)
63 {
64   current_ = this;
65   
66   global_frame_counter = 0;
67   game_pause = false;
68
69   fps_timer.init(true);            
70   frame_timer.init(true);
71
72   restart_level();
73 }
74
75 void
76 GameSession::restart_level()
77 {
78   game_pause   = false;
79   exit_status  = NONE;
80   end_sequence = NO_ENDSEQUENCE;
81
82   fps_timer.init(true);
83   frame_timer.init(true);
84
85   float old_x_pos = -1;
86
87   if (world)
88     { // Tux has lost a life, so we try to respawn him at the nearest reset point
89       old_x_pos = world->get_tux()->base.x;
90     }
91   
92   delete world;
93
94   if (st_gl_mode == ST_GL_LOAD_LEVEL_FILE)
95     {
96       world = new World(subset);
97     }
98   else if (st_gl_mode == ST_GL_DEMO_GAME)
99     {
100       world = new World(subset);
101     }
102   else
103     {
104       world = new World(subset, levelnb);
105     }
106
107   // Set Tux to the nearest reset point
108   if (old_x_pos != -1)
109     {
110       ResetPoint best_reset_point = { -1, -1 };
111       for(std::vector<ResetPoint>::iterator i = get_level()->reset_points.begin();
112           i != get_level()->reset_points.end(); ++i)
113         {
114           if (i->x < old_x_pos && best_reset_point.x < i->x)
115             best_reset_point = *i;
116         }
117       
118       if (best_reset_point.x != -1)
119         {
120           world->get_tux()->base.x = best_reset_point.x;
121           world->get_tux()->base.y = best_reset_point.y;
122         }
123     }
124     
125   if (st_gl_mode != ST_GL_DEMO_GAME)
126     {
127       if(st_gl_mode == ST_GL_PLAY || st_gl_mode == ST_GL_LOAD_LEVEL_FILE)
128         levelintro();
129     }
130
131   time_left.init(true);
132   start_timers();
133   world->play_music(LEVEL_MUSIC);
134 }
135
136 GameSession::~GameSession()
137 {
138   delete world;
139 }
140
141 void
142 GameSession::levelintro(void)
143 {
144   music_manager->halt_music();
145   
146   char str[60];
147  
148   if (get_level()->img_bkgd)
149     get_level()->img_bkgd->draw(0, 0);
150   else
151     drawgradient(get_level()->bkgd_top, get_level()->bkgd_bottom);
152
153   sprintf(str, "%s", world->get_level()->name.c_str());
154   gold_text->drawf(str, 0, 200, A_HMIDDLE, A_TOP, 1);
155
156   sprintf(str, "TUX x %d", player_status.lives);
157   white_text->drawf(str, 0, 224, A_HMIDDLE, A_TOP, 1);
158   
159   sprintf(str, "by %s", world->get_level()->author.c_str());
160   white_small_text->drawf(str, 0, 360, A_HMIDDLE, A_TOP, 1);
161   
162
163   flipscreen();
164
165   SDL_Event event;
166   wait_for_event(event,1000,3000,true);
167 }
168
169 /* Reset Timers */
170 void
171 GameSession::start_timers()
172 {
173   time_left.start(world->get_level()->time_left*1000);
174   st_pause_ticks_init();
175   update_time = st_get_ticks();
176 }
177
178 void
179 GameSession::on_escape_press()
180 {
181   if(game_pause)
182     return;
183
184   if(st_gl_mode == ST_GL_TEST)
185     {
186       exit_status = LEVEL_ABORT;
187     }
188   else if (!Menu::current())
189     {
190       Menu::set_current(game_menu);
191     }
192 }
193
194 void
195 GameSession::process_events()
196 {
197   if (end_sequence != NO_ENDSEQUENCE)
198     {
199       Player& tux = *world->get_tux();
200          
201       tux.input.fire  = UP;
202       tux.input.left  = UP;
203       tux.input.right = DOWN;
204       tux.input.down  = UP; 
205
206       if (int(last_x_pos) == int(tux.base.x))
207         tux.input.up    = DOWN; 
208       else
209         tux.input.up    = UP; 
210
211       last_x_pos = tux.base.x;
212
213       SDL_Event event;
214       while (SDL_PollEvent(&event))
215         {
216           /* Check for menu-events, if the menu is shown */
217           if (Menu::current())
218             {
219               Menu::current()->event(event);
220               st_pause_ticks_start();
221             }
222
223           switch(event.type)
224             {
225             case SDL_QUIT:        /* Quit event - quit: */
226               st_abort("Received window close", "");
227               break;
228               
229             case SDL_KEYDOWN:     /* A keypress! */
230               {
231                 SDLKey key = event.key.keysym.sym;
232            
233                 switch(key)
234                   {
235                   case SDLK_ESCAPE:    /* Escape: Open/Close the menu: */
236                     on_escape_press();
237                     break;
238                   default:
239                     break;
240                   }
241               }
242           
243             case SDL_JOYBUTTONDOWN:
244               if (event.jbutton.button == joystick_keymap.start_button)
245                 on_escape_press();
246               break;
247             }
248         }
249     }
250   else // normal mode
251     {
252       if(!Menu::current() && !game_pause)
253         st_pause_ticks_stop();
254
255       SDL_Event event;
256       while (SDL_PollEvent(&event))
257         {
258           /* Check for menu-events, if the menu is shown */
259           if (Menu::current())
260             {
261               Menu::current()->event(event);
262               st_pause_ticks_start();
263             }
264           else
265             {
266               Player& tux = *world->get_tux();
267   
268               switch(event.type)
269                 {
270                 case SDL_QUIT:        /* Quit event - quit: */
271                   st_abort("Received window close", "");
272                   break;
273
274                 case SDL_KEYDOWN:     /* A keypress! */
275                   {
276                     SDLKey key = event.key.keysym.sym;
277             
278                     if(tux.key_event(key,DOWN))
279                       break;
280
281                     switch(key)
282                       {
283                       case SDLK_ESCAPE:    /* Escape: Open/Close the menu: */
284                         on_escape_press();
285                         break;
286                       default:
287                         break;
288                       }
289                   }
290                   break;
291                 case SDL_KEYUP:      /* A keyrelease! */
292                   {
293                     SDLKey key = event.key.keysym.sym;
294
295                     if(tux.key_event(key, UP))
296                       break;
297
298                     switch(key)
299                       {
300                       case SDLK_p:
301                         if(!Menu::current())
302                           {
303                             if(game_pause)
304                               {
305                                 game_pause = false;
306                                 st_pause_ticks_stop();
307                               }
308                             else
309                               {
310                                 game_pause = true;
311                                 st_pause_ticks_start();
312                               }
313                           }
314                         break;
315                       case SDLK_TAB:
316                         if(debug_mode)
317                           {
318                             tux.size = !tux.size;
319                             if(tux.size == BIG)
320                               {
321                                 tux.base.height = 64;
322                               }
323                             else
324                               tux.base.height = 32;
325                           }
326                         break;
327                       case SDLK_END:
328                         if(debug_mode)
329                           player_status.distros += 50;
330                         break;
331                       case SDLK_DELETE:
332                         if(debug_mode)
333                           tux.got_coffee = 1;
334                         break;
335                       case SDLK_INSERT:
336                         if(debug_mode)
337                           tux.invincible_timer.start(TUX_INVINCIBLE_TIME);
338                         break;
339                       case SDLK_l:
340                         if(debug_mode)
341                           --player_status.lives;
342                         break;
343                       case SDLK_s:
344                         if(debug_mode)
345                           player_status.score += 1000;
346                       case SDLK_f:
347                         if(debug_fps)
348                           debug_fps = false;
349                         else
350                           debug_fps = true;
351                         break;
352                       default:
353                         break;
354                       }
355                   }
356                   break;
357
358                 case SDL_JOYAXISMOTION:
359                   if (event.jaxis.axis == joystick_keymap.x_axis)
360                     {
361                       if (event.jaxis.value < -joystick_keymap.dead_zone)
362                         {
363                           tux.input.left  = DOWN;
364                           tux.input.right = UP;
365                         }
366                       else if (event.jaxis.value > joystick_keymap.dead_zone)
367                         {
368                           tux.input.left  = UP;
369                           tux.input.right = DOWN;
370                         }
371                       else
372                         {
373                           tux.input.left  = DOWN;
374                           tux.input.right = DOWN;
375                         }
376                     }
377                   else if (event.jaxis.axis == joystick_keymap.y_axis)
378                     {
379                       if (event.jaxis.value > joystick_keymap.dead_zone)
380                         tux.input.down = DOWN;
381                       else if (event.jaxis.value < -joystick_keymap.dead_zone)
382                         tux.input.down = UP;
383                       else
384                         tux.input.down = UP;
385                     }
386                   break;
387             
388                 case SDL_JOYBUTTONDOWN:
389                   if (event.jbutton.button == joystick_keymap.a_button)
390                     tux.input.up = DOWN;
391                   else if (event.jbutton.button == joystick_keymap.b_button)
392                     tux.input.fire = DOWN;
393                   else if (event.jbutton.button == joystick_keymap.start_button)
394                     on_escape_press();
395                   break;
396                 case SDL_JOYBUTTONUP:
397                   if (event.jbutton.button == joystick_keymap.a_button)
398                     tux.input.up = UP;
399                   else if (event.jbutton.button == joystick_keymap.b_button)
400                     tux.input.fire = UP;
401                   break;
402
403                 default:
404                   break;
405                 }  /* switch */
406             }
407         } /* while */
408     }
409 }
410
411 void
412 GameSession::check_end_conditions()
413 {
414   Player* tux = world->get_tux();
415
416   /* End of level? */
417   int endpos = (World::current()->get_level()->width-5) * 32;
418   Tile* endtile = collision_goal(tux->base);
419
420   // fallback in case the other endpositions don't trigger
421   if (!end_sequence && tux->base.x >= endpos)
422     {
423       end_sequence = ENDSEQUENCE_WAITING;
424       last_x_pos = -1;
425       music_manager->play_music(level_end_song, 0);
426       endsequence_timer.start(7000);
427     }
428   else if(end_sequence && !endsequence_timer.check())
429     {
430       exit_status = LEVEL_FINISHED;
431       return;
432     }
433   else if(end_sequence == ENDSEQUENCE_RUNNING && endtile && endtile->data >= 1)
434     {
435       end_sequence = ENDSEQUENCE_WAITING;
436     }
437   else if(!end_sequence && endtile && endtile->data == 0)
438     {
439       end_sequence = ENDSEQUENCE_RUNNING;
440       last_x_pos = -1;
441       music_manager->play_music(level_end_song, 0);
442       endsequence_timer.start(7000); // 5 seconds until we finish the map
443     }
444   else if (!end_sequence && tux->is_dead())
445     {
446       player_status.bonus = PlayerStatus::NO_BONUS;
447       player_status.lives -= 1;             
448
449       if (player_status.lives < 0)
450         { // No more lives!?
451           if(st_gl_mode != ST_GL_TEST)
452             drawendscreen();
453           
454           exit_status = GAME_OVER;
455         }
456       else
457         { // Still has lives, so reset Tux to the levelstart
458           restart_level();
459         }
460
461       return;
462     }
463 }
464
465 void
466 GameSession::action(double frame_ratio)
467 {
468   if (exit_status == NONE)
469     {
470       // Update Tux and the World
471       world->action(frame_ratio);
472     }
473 }
474
475 void 
476 GameSession::draw()
477 {
478   world->draw();
479   drawstatus();
480
481   if(game_pause)
482     {
483       int x = screen->h / 20;
484       for(int i = 0; i < x; ++i)
485         {
486           fillrect(i % 2 ? (pause_menu_frame * i)%screen->w : -((pause_menu_frame * i)%screen->w) ,(i*20+pause_menu_frame)%screen->h,screen->w,10,20,20,20, rand() % 20 + 1);
487         }
488       fillrect(0,0,screen->w,screen->h,rand() % 50, rand() % 50, rand() % 50, 128);
489       blue_text->drawf("PAUSE - Press 'P' To Play", 0, 230, A_HMIDDLE, A_TOP, 1);
490     }
491
492   if(Menu::current())
493     {
494       Menu::current()->draw();
495       mouse_cursor->draw();
496     }
497
498   updatescreen();
499 }
500
501 void
502 GameSession::process_menu()
503 {
504   Menu* menu = Menu::current();
505   if(menu)
506     {
507       menu->action();
508
509       if(menu == game_menu)
510         {
511           switch (game_menu->check())
512             {
513             case MNID_CONTINUE:
514               st_pause_ticks_stop();
515               break;
516             case MNID_ABORTLEVEL:
517               st_pause_ticks_stop();
518               exit_status = LEVEL_ABORT;
519               break;
520             }
521         }
522       else if(menu == options_menu)
523         {
524           process_options_menu();
525         }
526       else if(menu == load_game_menu )
527         {
528           process_load_game_menu();
529         }
530     }
531 }
532
533 GameSession::ExitStatus
534 GameSession::run()
535 {
536   Menu::set_current(0);
537   current_ = this;
538   
539   int fps_cnt = 0;
540
541   update_time = last_update_time = st_get_ticks();
542
543   // Eat unneeded events
544   SDL_Event event;
545   while (SDL_PollEvent(&event)) {}
546
547   draw();
548
549   float overlap = 0.0f;
550   while (exit_status == NONE)
551     {
552       /* Calculate the movement-factor */
553       double frame_ratio = ((double)(update_time-last_update_time))/((double)FRAME_RATE);
554
555       if(!frame_timer.check())
556         {
557           frame_timer.start(25);
558           ++global_frame_counter;
559         }
560
561       /* Handle events: */
562       world->get_tux()->input.old_fire = world->get_tux()->input.fire;
563
564       process_events();
565       process_menu();
566
567       // Update the world state and all objects in the world
568       // Do that with a constante time-delta so that the game will run
569       // determistic and not different on different machines
570       if(!game_pause && !Menu::current())
571         {
572           frame_ratio *= game_speed;
573           frame_ratio += overlap;
574           while (frame_ratio > 0)
575             {
576               // Update the world
577               check_end_conditions();
578               if (end_sequence == ENDSEQUENCE_RUNNING)
579                 action(.5f);
580               else if(end_sequence == NO_ENDSEQUENCE)
581                 action(1.0f);
582               frame_ratio -= 1.0f;
583             }
584           overlap = frame_ratio;
585         }
586       else
587         {
588           ++pause_menu_frame;
589           SDL_Delay(50);
590         }
591
592       draw();
593
594       /* Time stops in pause mode */
595       if(game_pause || Menu::current())
596         {
597           continue;
598         }
599
600       /* Set the time of the last update and the time of the current update */
601       last_update_time = update_time;
602       update_time      = st_get_ticks();
603
604       /* Pause till next frame, if the machine running the game is too fast: */
605       /* FIXME: Works great for in OpenGl mode, where the CPU doesn't have to do that much. But
606          the results in SDL mode aren't perfect (thought the 100 FPS are reached), even on an AMD2500+. */
607       if(last_update_time >= update_time - 12) 
608         {
609           SDL_Delay(10);
610           update_time = st_get_ticks();
611         }
612
613       /* Handle time: */
614       if (!time_left.check() && world->get_tux()->dying == DYING_NOT
615               && !end_sequence)
616         world->get_tux()->kill(Player::KILL);
617
618       /* Handle music: */
619       if(world->get_tux()->invincible_timer.check() && !end_sequence)
620         {
621           world->play_music(HERRING_MUSIC);
622         }
623       /* are we low on time ? */
624       else if (time_left.get_left() < TIME_WARNING && !end_sequence)
625         {
626           world->play_music(HURRYUP_MUSIC);
627         }
628       /* or just normal music? */
629       else if(world->get_music_type() != LEVEL_MUSIC && !end_sequence)
630         {
631           world->play_music(LEVEL_MUSIC);
632         }
633
634       /* Calculate frames per second */
635       if(show_fps)
636         {
637           ++fps_cnt;
638           fps_fps = (1000.0 / (float)fps_timer.get_gone()) * (float)fps_cnt;
639
640           if(!fps_timer.check())
641             {
642               fps_timer.start(1000);
643               fps_cnt = 0;
644             }
645         }
646     }
647   
648   return exit_status;
649 }
650
651 /* Bounce a brick: */
652 void bumpbrick(float x, float y)
653 {
654   World::current()->add_bouncy_brick(((int)(x + 1) / 32) * 32,
655                          (int)(y / 32) * 32);
656
657   play_sound(sounds[SND_BRICK], SOUND_CENTER_SPEAKER);
658 }
659
660 /* (Status): */
661 void
662 GameSession::drawstatus()
663 {
664   char str[60];
665
666   sprintf(str, "%d", player_status.score);
667   white_text->draw("SCORE", 0, 0, 1);
668   gold_text->draw(str, 96, 0, 1);
669
670   if(st_gl_mode == ST_GL_TEST)
671     {
672       white_text->draw("Press ESC To Return",0,20,1);
673     }
674
675   if(!time_left.check()) {
676     white_text->draw("TIME'S UP", 224, 0, 1);
677   } else if (time_left.get_left() > TIME_WARNING || (global_frame_counter % 10) < 5) {
678     sprintf(str, "%d", time_left.get_left() / 1000 );
679     white_text->draw("TIME", 224, 0, 1);
680     gold_text->draw(str, 304, 0, 1);
681   }
682
683   sprintf(str, "%d", player_status.distros);
684   white_text->draw("COINS", screen->h, 0, 1);
685   gold_text->draw(str, 608, 0, 1);
686
687   white_text->draw("LIVES", 480, 20);
688   if (player_status.lives >= 5)
689     {
690       sprintf(str, "%dx", player_status.lives);
691       gold_text->draw_align(str, 617, 20, A_RIGHT, A_TOP);
692       tux_life->draw(565+(18*3), 20);
693     }
694   else
695     {
696       for(int i= 0; i < player_status.lives; ++i)
697         tux_life->draw(565+(18*i),20);
698     }
699
700   if(show_fps)
701     {
702       sprintf(str, "%2.1f", fps_fps);
703       white_text->draw("FPS", screen->h, 40, 1);
704       gold_text->draw(str, screen->h + 60, 40, 1);
705     }
706 }
707
708 void
709 GameSession::drawendscreen()
710 {
711   char str[80];
712
713   if (get_level()->img_bkgd)
714     get_level()->img_bkgd->draw(0, 0);
715   else
716     drawgradient(get_level()->bkgd_top, get_level()->bkgd_bottom);
717
718   blue_text->drawf("GAMEOVER", 0, 200, A_HMIDDLE, A_TOP, 1);
719
720   sprintf(str, "SCORE: %d", player_status.score);
721   gold_text->drawf(str, 0, 224, A_HMIDDLE, A_TOP, 1);
722
723   sprintf(str, "COINS: %d", player_status.distros);
724   gold_text->drawf(str, 0, 256, A_HMIDDLE, A_TOP, 1);
725
726   flipscreen();
727   
728   SDL_Event event;
729   wait_for_event(event,2000,5000,true);
730 }
731
732 void
733 GameSession::drawresultscreen(void)
734 {
735   char str[80];
736
737   if (get_level()->img_bkgd)
738     get_level()->img_bkgd->draw(0, 0);
739   else
740     drawgradient(get_level()->bkgd_top, get_level()->bkgd_bottom);
741
742   blue_text->drawf("Result:", 0, 200, A_HMIDDLE, A_TOP, 1);
743
744   sprintf(str, "SCORE: %d", player_status.score);
745   gold_text->drawf(str, 0, 224, A_HMIDDLE, A_TOP, 1);
746
747   sprintf(str, "COINS: %d", player_status.distros);
748   gold_text->drawf(str, 0, 256, A_HMIDDLE, A_TOP, 1);
749
750   flipscreen();
751   
752   SDL_Event event;
753   wait_for_event(event,2000,5000,true);
754 }
755
756 std::string slotinfo(int slot)
757 {
758   char tmp[1024];
759   char slotfile[1024];
760   std::string title;
761   sprintf(slotfile,"%s/slot%d.stsg",st_save_dir,slot);
762
763   lisp_object_t* savegame = lisp_read_from_file(slotfile);
764   if (savegame)
765     {
766       LispReader reader(lisp_cdr(savegame));
767       reader.read_string("title", &title);
768       lisp_free(savegame);
769     }
770
771   if (access(slotfile, F_OK) == 0)
772     {
773       if (!title.empty())
774         snprintf(tmp,1024,"Slot %d - %s",slot, title.c_str());
775       else
776         snprintf(tmp, 1024,"Slot %d - Savegame",slot);
777     }
778   else
779     sprintf(tmp,"Slot %d - Free",slot);
780
781   return tmp;
782 }
783
784