- localized some more variables
[supertux.git] / src / gameloop.cpp
1 /*
2   gameloop.c
3   
4   Super Tux - Game Loop!
5   
6   by Bill Kendrick & Tobias Glaesser
7   bill@newbreedsoftware.com
8   http://www.newbreedsoftware.com/supertux/
9   
10   April 11, 2000 - March 15, 2004
11 */
12
13 #include <assert.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <math.h>
17 #include <string.h>
18 #include <errno.h>
19 #include <unistd.h>
20 #include <math.h>
21 #include <time.h>
22 #include <SDL.h>
23
24 #ifndef WIN32
25 #include <sys/types.h>
26 #include <ctype.h>
27 #endif
28
29 #include "defines.h"
30 #include "globals.h"
31 #include "gameloop.h"
32 #include "screen.h"
33 #include "setup.h"
34 #include "high_scores.h"
35 #include "menu.h"
36 #include "badguy.h"
37 #include "world.h"
38 #include "special.h"
39 #include "player.h"
40 #include "level.h"
41 #include "scene.h"
42 #include "collision.h"
43 #include "tile.h"
44 #include "particlesystem.h"
45 #include "resources.h"
46
47 GameSession* GameSession::current_ = 0;
48
49 void
50 GameSession::init()
51 {
52   game_pause = false;
53 }
54
55 GameSession::GameSession()
56 {
57   current_ = this;
58   assert(0);
59 }
60
61 GameSession::GameSession(const std::string& filename)
62 {
63   init();
64
65   //assert(!"Don't call me");
66   current_ = this;
67
68   world = new World; // &::global_world;
69
70   timer_init(&fps_timer, true);
71   timer_init(&frame_timer, true);
72
73   world->load(filename);
74 }
75
76 GameSession::GameSession(const std::string& subset_, int levelnb_, int mode)
77   : subset(subset_),
78     levelnb(levelnb_)
79 {
80   init();
81
82   current_ = this;
83
84   world = new World; // &::global_world;
85
86   timer_init(&fps_timer, true);
87   timer_init(&frame_timer, true);
88
89   st_gl_mode = mode;
90   
91   /* Init the game: */
92   world->arrays_free();
93   world->set_defaults();
94
95   if (st_gl_mode == ST_GL_LOAD_LEVEL_FILE)
96     {
97       if (world->load(subset))
98         exit(1);
99     }
100   else
101     {
102       if(world->load(subset, levelnb) != 0)
103         exit(1);
104     }
105
106   world->get_level()->load_gfx();
107   loadshared();
108   
109   world->activate_bad_guys();
110   world->activate_particle_systems();
111   world->get_level()->load_song();
112
113   if(st_gl_mode != ST_GL_TEST)
114     load_hs();
115
116   if(st_gl_mode == ST_GL_PLAY || st_gl_mode == ST_GL_LOAD_LEVEL_FILE)
117     levelintro();
118
119   timer_init(&time_left,true);
120   start_timers();
121
122   if(st_gl_mode == ST_GL_LOAD_GAME)
123     loadgame(levelnb);
124 }
125
126 GameSession::~GameSession()
127 {
128   delete world;
129 }
130
131 void
132 GameSession::levelintro(void)
133 {
134   Player& tux = *world->get_tux();
135
136   char str[60];
137   /* Level Intro: */
138   clearscreen(0, 0, 0);
139
140   sprintf(str, "LEVEL %d", levelnb);
141   text_drawf(&blue_text, str, 0, 200, A_HMIDDLE, A_TOP, 1);
142
143   sprintf(str, "%s", world->get_level()->name.c_str());
144   text_drawf(&gold_text, str, 0, 224, A_HMIDDLE, A_TOP, 1);
145
146   sprintf(str, "TUX x %d", tux.lives);
147   text_drawf(&white_text, str, 0, 256, A_HMIDDLE, A_TOP, 1);
148
149   flipscreen();
150
151   SDL_Event event;
152   wait_for_event(event,1000,3000,true);
153 }
154
155 /* Reset Timers */
156 void
157 GameSession::start_timers()
158 {
159   timer_start(&time_left, world->get_level()->time_left*1000);
160   st_pause_ticks_init();
161   update_time = st_get_ticks();
162 }
163
164 void
165 GameSession::process_events()
166 {
167   Player& tux = *world->get_tux();
168
169   SDL_Event event;
170   while (SDL_PollEvent(&event))
171     {
172       /* Check for menu-events, if the menu is shown */
173       if(show_menu)
174         menu_event(event);
175
176       switch(event.type)
177         {
178         case SDL_QUIT:        /* Quit event - quit: */
179           quit = true;
180           break;
181         case SDL_KEYDOWN:     /* A keypress! */
182           {
183             SDLKey key = event.key.keysym.sym;
184             
185             if(tux.key_event(key,DOWN))
186               break;
187
188             switch(key)
189               {
190               case SDLK_ESCAPE:    /* Escape: Open/Close the menu: */
191                 if(!game_pause)
192                   {
193                     if(st_gl_mode == ST_GL_TEST)
194                       quit = true;
195                     else if(show_menu)
196                       {
197                         Menu::set_current(game_menu);
198                         show_menu = 0;
199                         st_pause_ticks_stop();
200                       }
201                     else
202                       {
203                         Menu::set_current(game_menu);
204                         show_menu = 1;
205                         st_pause_ticks_start();
206                       }
207                   }
208                 break;
209               default:
210                 break;
211               }
212           }
213           break;
214         case SDL_KEYUP:      /* A keyrelease! */
215           {
216             SDLKey key = event.key.keysym.sym;
217
218             if(tux.key_event(key, UP))
219               break;
220
221             switch(key)
222               {
223               case SDLK_p:
224                 if(!show_menu)
225                   {
226                     if(game_pause)
227                       {
228                         game_pause = false;
229                         st_pause_ticks_stop();
230                       }
231                     else
232                       {
233                         game_pause = true;
234                         st_pause_ticks_start();
235                       }
236                   }
237                 break;
238               case SDLK_TAB:
239                 if(debug_mode)
240                   {
241                     tux.size = !tux.size;
242                     if(tux.size == BIG)
243                       {
244                         tux.base.height = 64;
245                       }
246                     else
247                       tux.base.height = 32;
248                   }
249                 break;
250               case SDLK_END:
251                 if(debug_mode)
252                   distros += 50;
253                 break;
254               case SDLK_SPACE:
255                 if(debug_mode)
256                   next_level = 1;
257                 break;
258               case SDLK_DELETE:
259                 if(debug_mode)
260                   tux.got_coffee = 1;
261                 break;
262               case SDLK_INSERT:
263                 if(debug_mode)
264                   timer_start(&tux.invincible_timer,TUX_INVINCIBLE_TIME);
265                 break;
266               case SDLK_l:
267                 if(debug_mode)
268                   --tux.lives;
269                 break;
270               case SDLK_s:
271                 if(debug_mode)
272                   score += 1000;
273               case SDLK_f:
274                 if(debug_fps)
275                   debug_fps = false;
276                 else
277                   debug_fps = true;
278                 break;
279               default:
280                 break;
281               }
282           }
283           break;
284
285         case SDL_JOYAXISMOTION:
286           switch(event.jaxis.axis)
287             {
288             case JOY_X:
289               if (event.jaxis.value < -JOYSTICK_DEAD_ZONE)
290                 {
291                   tux.input.left  = DOWN;
292                   tux.input.right = UP;
293                 }
294               else if (event.jaxis.value > JOYSTICK_DEAD_ZONE)
295                 {
296                   tux.input.left  = UP;
297                   tux.input.right = DOWN;
298                 }
299               else
300                 {
301                   tux.input.left  = DOWN;
302                   tux.input.right = DOWN;
303                 }
304               break;
305             case JOY_Y:
306               if (event.jaxis.value > JOYSTICK_DEAD_ZONE)
307                 tux.input.down = DOWN;
308               else if (event.jaxis.value < -JOYSTICK_DEAD_ZONE)
309                 tux.input.down = UP;
310               else
311                 tux.input.down = UP;
312               
313               break;
314             default:
315               break;
316             }
317           break;
318         case SDL_JOYBUTTONDOWN:
319           if (event.jbutton.button == JOY_A)
320             tux.input.up = DOWN;
321           else if (event.jbutton.button == JOY_B)
322             tux.input.fire = DOWN;
323           break;
324         case SDL_JOYBUTTONUP:
325           if (event.jbutton.button == JOY_A)
326             tux.input.up = UP;
327           else if (event.jbutton.button == JOY_B)
328             tux.input.fire = UP;
329             
330           break;
331
332         default:
333           break;
334
335         }  /* switch */
336
337     } /* while */
338 }
339
340 int
341 GameSession::action()
342 {
343   Player& tux = *world->get_tux();
344
345   if (tux.is_dead() || next_level)
346     {
347       /* Tux either died, or reached the end of a level! */
348       halt_music();
349       
350       if (next_level)
351         {
352           /* End of a level! */
353           levelnb++;
354           next_level = 0;
355           if(st_gl_mode != ST_GL_TEST)
356             {
357               drawresultscreen();
358             }
359           else
360             {
361               world->get_level()->free_gfx();
362               world->get_level()->cleanup();
363               world->get_level()->free_song();
364               world->arrays_free();
365
366               unloadshared();
367               return(0);
368             }
369           tux.level_begin();
370         }
371       else
372         {
373           tux.is_dying();
374
375           /* No more lives!? */
376
377           if (tux.lives < 0)
378             {
379               if(st_gl_mode != ST_GL_TEST)
380                 drawendscreen();
381
382               if(st_gl_mode != ST_GL_TEST)
383                 {
384                   if (score > hs_score)
385                     save_hs(score);
386                 }
387
388               world->get_level()->free_gfx();
389               world->get_level()->cleanup();
390               world->get_level()->free_song();
391               world->arrays_free();
392
393               unloadshared();
394               return(0);
395             } /* if (lives < 0) */
396         }
397
398       /* Either way, (re-)load the (next) level... */
399       tux.level_begin();
400       world->set_defaults();
401       
402       world->get_level()->cleanup();
403
404       if (st_gl_mode == ST_GL_LOAD_LEVEL_FILE)
405         {
406           if(world->get_level()->load(subset) != 0)
407             return 0;
408         }
409       else
410         {
411           if(world->get_level()->load(subset, levelnb) != 0)
412             return 0;
413         }
414
415       world->arrays_free();
416       world->activate_bad_guys();
417       world->activate_particle_systems();
418
419       world->get_level()->free_gfx();
420       world->get_level()->load_gfx();
421       world->get_level()->free_song();
422       world->get_level()->load_song();
423
424       if(st_gl_mode != ST_GL_TEST)
425         levelintro();
426       start_timers();
427       /* Play music: */
428       play_current_music();
429     }
430
431   tux.action();
432
433   world->action();
434
435   return -1;
436 }
437
438 void 
439 GameSession::draw()
440 {
441   world->draw();
442   drawstatus();
443
444   if(game_pause)
445     {
446       int x = screen->h / 20;
447       for(int i = 0; i < x; ++i)
448         {
449           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);
450         }
451       fillrect(0,0,screen->w,screen->h,rand() % 50, rand() % 50, rand() % 50, 128);
452       text_drawf(&blue_text, "PAUSE - Press 'P' To Play", 0, 230, A_HMIDDLE, A_TOP, 1);
453     }
454
455   if(show_menu)
456     {
457       menu_process_current();
458       mouse_cursor->draw();
459     }
460
461   updatescreen();
462 }
463
464
465 int
466 GameSession::run()
467 {
468   Player& tux = *world->get_tux();
469   current_ = this;
470   
471   int  fps_cnt;
472   bool done;
473
474   global_frame_counter = 0;
475   game_pause = false;
476   timer_init(&fps_timer,true);
477   timer_init(&frame_timer,true);
478   last_update_time = st_get_ticks();
479   fps_cnt = 0;
480
481   /* Clear screen: */
482   clearscreen(0, 0, 0);
483   updatescreen();
484
485   /* Play music: */
486   play_current_music();
487
488   // Eat unneeded events
489   SDL_Event event;
490   while (SDL_PollEvent(&event)) {}
491
492   draw();
493
494   done = false;
495   quit = false;
496   while (!done && !quit)
497     {
498       /* Calculate the movement-factor */
499       frame_ratio = ((double)(update_time-last_update_time))/((double)FRAME_RATE);
500       if(frame_ratio > 1.5) /* Quick hack to correct the unprecise CPU clocks a little bit. */
501         frame_ratio = 1.5 + (frame_ratio - 1.5) * 0.85;
502
503       if(!timer_check(&frame_timer))
504         {
505           timer_start(&frame_timer,25);
506           ++global_frame_counter;
507         }
508
509       /* Handle events: */
510       tux.input.old_fire = tux.input.fire;
511
512       process_events();
513
514       if(show_menu)
515         {
516           if(current_menu == game_menu)
517             {
518               switch (game_menu->check())
519                 {
520                 case 2:
521                   st_pause_ticks_stop();
522                   break;
523                 case 3:
524                   update_load_save_game_menu(save_game_menu, false);
525                   break;
526                 case 4:
527                   update_load_save_game_menu(load_game_menu, true);
528                   break;
529                 case 7:
530                   st_pause_ticks_stop();
531                   done = true;
532                   break;
533                 }
534             }
535           else if(current_menu == options_menu)
536             {
537               process_options_menu();
538             }
539           else if(current_menu == save_game_menu )
540             {
541               process_save_game_menu();
542             }
543           else if(current_menu == load_game_menu )
544             {
545               process_load_game_menu();
546             }
547         }
548
549
550       /* Handle actions: */
551
552       if(!game_pause && !show_menu)
553         {
554           /*float z = frame_ratio;
555             frame_ratio = 1;
556             while(z >= 1)
557             {*/
558           if (action() == 0)
559             {
560               /* == 0: no more lives */
561               /* == -1: continues */
562               return 0;
563             }
564           /*  --z;
565                      }*/
566         }
567       else
568         {
569           ++pause_menu_frame;
570           SDL_Delay(50);
571         }
572
573       if(debug_mode && debug_fps)
574         SDL_Delay(60);
575
576       /*Draw the current scene to the screen */
577       /*If the machine running the game is too slow
578         skip the drawing of the frame (so the calculations are more precise and
579         the FPS aren't affected).*/
580       /*if( ! fps_fps < 50.0 )
581         game_draw();
582         else
583         jump = true;*/ /*FIXME: Implement this tweak right.*/
584       draw();
585
586       /* Time stops in pause mode */
587       if(game_pause || show_menu )
588         {
589           continue;
590         }
591
592       /* Set the time of the last update and the time of the current update */
593       last_update_time = update_time;
594       update_time = st_get_ticks();
595
596       /* Pause till next frame, if the machine running the game is too fast: */
597       /* FIXME: Works great for in OpenGl mode, where the CPU doesn't have to do that much. But
598          the results in SDL mode aren't perfect (thought the 100 FPS are reached), even on an AMD2500+. */
599       if(last_update_time >= update_time - 12) {
600         SDL_Delay(10);
601         update_time = st_get_ticks();
602       }
603       /*if((update_time - last_update_time) < 10)
604         SDL_Delay((11 - (update_time - last_update_time))/2);*/
605
606       /* Handle time: */
607       if (timer_check(&time_left))
608         {
609           /* are we low on time ? */
610           if ((timer_get_left(&time_left) < TIME_WARNING)
611               && (get_current_music() != HURRYUP_MUSIC))     /* play the fast music */
612             {
613               set_current_music(HURRYUP_MUSIC);
614               play_current_music();
615             }
616
617         }
618       else
619         tux.kill(KILL);
620
621       /* Calculate frames per second */
622       if(show_fps)
623         {
624           ++fps_cnt;
625           fps_fps = (1000.0 / (float)timer_get_gone(&fps_timer)) * (float)fps_cnt;
626
627           if(!timer_check(&fps_timer))
628             {
629               timer_start(&fps_timer,1000);
630               fps_cnt = 0;
631             }
632         }
633     }
634
635   halt_music();
636
637   world->get_level()->free_gfx();
638   world->get_level()->cleanup();
639   world->get_level()->free_song();
640
641   unloadshared();
642   world->arrays_free();
643
644   return quit;
645 }
646
647 /* Bounce a brick: */
648 void bumpbrick(float x, float y)
649 {
650   World::current()->add_bouncy_brick(((int)(x + 1) / 32) * 32,
651                          (int)(y / 32) * 32);
652
653   play_sound(sounds[SND_BRICK], SOUND_CENTER_SPEAKER);
654 }
655
656 /* (Status): */
657 void
658 GameSession::drawstatus()
659 {
660   Player& tux = *world->get_tux();
661   char str[60];
662
663   sprintf(str, "%d", score);
664   text_draw(&white_text, "SCORE", 0, 0, 1);
665   text_draw(&gold_text, str, 96, 0, 1);
666
667   if(st_gl_mode != ST_GL_TEST)
668     {
669       sprintf(str, "%d", hs_score);
670       text_draw(&white_text, "HIGH", 0, 20, 1);
671       text_draw(&gold_text, str, 96, 20, 1);
672     }
673   else
674     {
675       text_draw(&white_text,"Press ESC To Return",0,20,1);
676     }
677
678   if (timer_get_left(&time_left) > TIME_WARNING || (global_frame_counter % 10) < 5)
679     {
680       sprintf(str, "%d", timer_get_left(&time_left) / 1000 );
681       text_draw(&white_text, "TIME", 224, 0, 1);
682       text_draw(&gold_text, str, 304, 0, 1);
683     }
684
685   sprintf(str, "%d", distros);
686   text_draw(&white_text, "DISTROS", screen->h, 0, 1);
687   text_draw(&gold_text, str, 608, 0, 1);
688
689   text_draw(&white_text, "LIVES", screen->h, 20, 1);
690
691   if(show_fps)
692     {
693       sprintf(str, "%2.1f", fps_fps);
694       text_draw(&white_text, "FPS", screen->h, 40, 1);
695       text_draw(&gold_text, str, screen->h + 60, 40, 1);
696     }
697
698   for(int i= 0; i < tux.lives; ++i)
699     {
700       texture_draw(&tux_life,565+(18*i),20);
701     }
702 }
703
704 void
705 GameSession::drawendscreen()
706 {
707   char str[80];
708
709   clearscreen(0, 0, 0);
710
711   text_drawf(&blue_text, "GAMEOVER", 0, 200, A_HMIDDLE, A_TOP, 1);
712
713   sprintf(str, "SCORE: %d", score);
714   text_drawf(&gold_text, str, 0, 224, A_HMIDDLE, A_TOP, 1);
715
716   sprintf(str, "DISTROS: %d", distros);
717   text_drawf(&gold_text, str, 0, 256, A_HMIDDLE, A_TOP, 1);
718
719   flipscreen();
720   
721   SDL_Event event;
722   wait_for_event(event,2000,5000,true);
723 }
724
725 void
726 GameSession::drawresultscreen(void)
727 {
728   char str[80];
729
730   clearscreen(0, 0, 0);
731
732   text_drawf(&blue_text, "Result:", 0, 200, A_HMIDDLE, A_TOP, 1);
733
734   sprintf(str, "SCORE: %d", score);
735   text_drawf(&gold_text, str, 0, 224, A_HMIDDLE, A_TOP, 1);
736
737   sprintf(str, "DISTROS: %d", distros);
738   text_drawf(&gold_text, str, 0, 256, A_HMIDDLE, A_TOP, 1);
739
740   flipscreen();
741   
742   SDL_Event event;
743   wait_for_event(event,2000,5000,true);
744 }
745
746 void
747 GameSession::savegame(int)
748 {
749 #if 0
750   char savefile[1024];
751   FILE* fi;
752   unsigned int ui;
753
754   sprintf(savefile,"%s/slot%d.save",st_save_dir,slot);
755
756   fi = fopen(savefile, "wb");
757
758   if (fi == NULL)
759     {
760       fprintf(stderr, "Warning: I could not open the slot file ");
761     }
762   else
763     {
764       fputs(level_subset, fi);
765       fputs("\n", fi);
766       fwrite(&level,sizeof(int),1,fi);
767       fwrite(&score,sizeof(int),1,fi);
768       fwrite(&distros,sizeof(int),1,fi);
769       fwrite(&scroll_x,sizeof(float),1,fi);
770       //FIXME:fwrite(&tux,sizeof(Player),1,fi);
771       //FIXME:timer_fwrite(&tux.invincible_timer,fi);
772       //FIXME:timer_fwrite(&tux.skidding_timer,fi);
773       //FIXME:timer_fwrite(&tux.safe_timer,fi);
774       //FIXME:timer_fwrite(&tux.frame_timer,fi);
775       timer_fwrite(&time_left,fi);
776       ui = st_get_ticks();
777       fwrite(&ui,sizeof(int),1,fi);
778     }
779   fclose(fi);
780 #endif 
781 }
782
783 void
784 GameSession::loadgame(int)
785 {
786 #if 0
787   char savefile[1024];
788   char str[100];
789   FILE* fi;
790   unsigned int ui;
791
792   sprintf(savefile,"%s/slot%d.save",st_save_dir,slot);
793
794   fi = fopen(savefile, "rb");
795
796   if (fi == NULL)
797     {
798       fprintf(stderr, "Warning: I could not open the slot file ");
799
800     }
801   else
802     {
803       fgets(str, 100, fi);
804       strcpy(level_subset, str);
805       level_subset[strlen(level_subset)-1] = '\0';
806       fread(&level,sizeof(int),1,fi);
807
808       world->set_defaults();
809       world->get_level()->cleanup();
810       world->arrays_free();
811       world->get_level()->free_gfx();
812       world->get_level()->free_song();
813
814       if(world->get_level()->load(level_subset,level) != 0)
815         exit(1);
816
817       world->activate_bad_guys();
818       world->activate_particle_systems();
819       world->get_level()->load_gfx();
820       world->get_level()->load_song();
821
822       levelintro();
823       update_time = st_get_ticks();
824
825       fread(&score,   sizeof(int),1,fi);
826       fread(&distros, sizeof(int),1,fi);
827       fread(&scroll_x,sizeof(float),1,fi);
828       //FIXME:fread(&tux,     sizeof(Player), 1, fi);
829       //FIXME:timer_fread(&tux.invincible_timer,fi);
830       //FIXME:timer_fread(&tux.skidding_timer,fi);
831       //FIXME:timer_fread(&tux.safe_timer,fi);
832       //FIXME:timer_fread(&tux.frame_timer,fi);
833       timer_fread(&time_left,fi);
834       fread(&ui,sizeof(int),1,fi);
835       fclose(fi);
836     }
837 #endif 
838 }
839
840 std::string slotinfo(int slot)
841 {
842   FILE* fi;
843   char slotfile[1024];
844   char tmp[200];
845   char str[5];
846   int slot_level;
847   sprintf(slotfile,"%s/slot%d.save",st_save_dir,slot);
848
849   fi = fopen(slotfile, "rb");
850
851   sprintf(tmp,"Slot %d - ",slot);
852
853   if (fi == NULL)
854     {
855       strcat(tmp,"Free");
856     }
857   else
858     {
859       fgets(str, 100, fi);
860       str[strlen(str)-1] = '\0';
861       strcat(tmp, str);
862       strcat(tmp, " / Level:");
863       fread(&slot_level,sizeof(int),1,fi);
864       sprintf(str,"%d",slot_level);
865       strcat(tmp,str);
866       fclose(fi);
867     }
868
869   return tmp;
870 }
871