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