6 by Bill Kendrick & Tobias Glaesser
7 bill@newbreedsoftware.com
8 http://www.newbreedsoftware.com/supertux/
10 April 11, 2000 - March 15, 2004
25 #include <sys/types.h>
34 #include "high_scores.h"
42 #include "collision.h"
44 #include "particlesystem.h"
45 #include "resources.h"
47 GameSession* GameSession::current_ = 0;
55 GameSession::GameSession()
61 GameSession::GameSession(const std::string& filename)
65 //assert(!"Don't call me");
68 world = new World; // &::global_world;
71 frame_timer.init(true);
73 world->load(filename);
76 GameSession::GameSession(const std::string& subset_, int levelnb_, int mode)
84 world = new World; // &::global_world;
87 frame_timer.init(true);
93 world->set_defaults();
95 if (st_gl_mode == ST_GL_LOAD_LEVEL_FILE)
97 if (world->load(subset))
102 if(world->load(subset, levelnb) != 0)
106 world->get_level()->load_gfx();
109 world->activate_bad_guys();
110 world->activate_particle_systems();
111 world->get_level()->load_song();
113 if(st_gl_mode != ST_GL_TEST)
116 if(st_gl_mode == ST_GL_PLAY || st_gl_mode == ST_GL_LOAD_LEVEL_FILE)
119 time_left.init(true);
122 if(st_gl_mode == ST_GL_LOAD_GAME)
126 GameSession::~GameSession()
132 GameSession::levelintro(void)
134 Player& tux = *world->get_tux();
138 clearscreen(0, 0, 0);
140 sprintf(str, "LEVEL %d", levelnb);
141 text_drawf(&blue_text, str, 0, 200, A_HMIDDLE, A_TOP, 1);
143 sprintf(str, "%s", world->get_level()->name.c_str());
144 text_drawf(&gold_text, str, 0, 224, A_HMIDDLE, A_TOP, 1);
146 sprintf(str, "TUX x %d", tux.lives);
147 text_drawf(&white_text, str, 0, 256, A_HMIDDLE, A_TOP, 1);
152 wait_for_event(event,1000,3000,true);
157 GameSession::start_timers()
159 time_left.start(world->get_level()->time_left*1000);
160 st_pause_ticks_init();
161 update_time = st_get_ticks();
165 GameSession::process_events()
167 Player& tux = *world->get_tux();
170 while (SDL_PollEvent(&event))
172 /* Check for menu-events, if the menu is shown */
178 case SDL_QUIT: /* Quit event - quit: */
181 case SDL_KEYDOWN: /* A keypress! */
183 SDLKey key = event.key.keysym.sym;
185 if(tux.key_event(key,DOWN))
190 case SDLK_ESCAPE: /* Escape: Open/Close the menu: */
193 if(st_gl_mode == ST_GL_TEST)
197 Menu::set_current(game_menu);
199 st_pause_ticks_stop();
203 Menu::set_current(game_menu);
205 st_pause_ticks_start();
214 case SDL_KEYUP: /* A keyrelease! */
216 SDLKey key = event.key.keysym.sym;
218 if(tux.key_event(key, UP))
229 st_pause_ticks_stop();
234 st_pause_ticks_start();
241 tux.size = !tux.size;
244 tux.base.height = 64;
247 tux.base.height = 32;
252 player_status.distros += 50;
256 player_status.next_level = 1;
264 tux.invincible_timer.start(TUX_INVINCIBLE_TIME);
272 player_status.score += 1000;
285 case SDL_JOYAXISMOTION:
286 switch(event.jaxis.axis)
289 if (event.jaxis.value < -JOYSTICK_DEAD_ZONE)
291 tux.input.left = DOWN;
292 tux.input.right = UP;
294 else if (event.jaxis.value > JOYSTICK_DEAD_ZONE)
297 tux.input.right = DOWN;
301 tux.input.left = DOWN;
302 tux.input.right = DOWN;
306 if (event.jaxis.value > JOYSTICK_DEAD_ZONE)
307 tux.input.down = DOWN;
308 else if (event.jaxis.value < -JOYSTICK_DEAD_ZONE)
318 case SDL_JOYBUTTONDOWN:
319 if (event.jbutton.button == JOY_A)
321 else if (event.jbutton.button == JOY_B)
322 tux.input.fire = DOWN;
324 case SDL_JOYBUTTONUP:
325 if (event.jbutton.button == JOY_A)
327 else if (event.jbutton.button == JOY_B)
341 GameSession::action(double frame_ratio)
343 Player& tux = *world->get_tux();
345 if (tux.is_dead() || player_status.next_level)
347 /* Tux either died, or reached the end of a level! */
350 if (player_status.next_level)
352 /* End of a level! */
354 player_status.next_level = 0;
355 if(st_gl_mode != ST_GL_TEST)
361 world->get_level()->free_gfx();
362 world->get_level()->cleanup();
363 world->get_level()->free_song();
364 world->arrays_free();
375 /* No more lives!? */
379 if(st_gl_mode != ST_GL_TEST)
382 if(st_gl_mode != ST_GL_TEST)
384 if (player_status.score > hs_score)
385 save_hs(player_status.score);
388 world->get_level()->free_gfx();
389 world->get_level()->cleanup();
390 world->get_level()->free_song();
391 world->arrays_free();
395 } /* if (lives < 0) */
398 /* Either way, (re-)load the (next) level... */
400 world->set_defaults();
402 world->get_level()->cleanup();
404 if (st_gl_mode == ST_GL_LOAD_LEVEL_FILE)
406 if(world->get_level()->load(subset) != 0)
411 if(world->get_level()->load(subset, levelnb) != 0)
415 world->arrays_free();
416 world->activate_bad_guys();
417 world->activate_particle_systems();
419 world->get_level()->free_gfx();
420 world->get_level()->load_gfx();
421 world->get_level()->free_song();
422 world->get_level()->load_song();
424 if(st_gl_mode != ST_GL_TEST)
428 play_current_music();
431 tux.action(frame_ratio);
433 world->action(frame_ratio);
446 int x = screen->h / 20;
447 for(int i = 0; i < x; ++i)
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);
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);
457 menu_process_current();
458 mouse_cursor->draw();
468 Player& tux = *world->get_tux();
474 global_frame_counter = 0;
477 fps_timer.init(true);
478 frame_timer.init(true);
480 last_update_time = st_get_ticks();
484 clearscreen(0, 0, 0);
488 play_current_music();
490 // Eat unneeded events
492 while (SDL_PollEvent(&event)) {}
498 while (!done && !quit)
500 /* Calculate the movement-factor */
501 double frame_ratio = ((double)(update_time-last_update_time))/((double)FRAME_RATE);
502 if(frame_ratio > 1.5) /* Quick hack to correct the unprecise CPU clocks a little bit. */
503 frame_ratio = 1.5 + (frame_ratio - 1.5) * 0.85;
505 if(!frame_timer.check())
507 frame_timer.start(25);
508 ++global_frame_counter;
512 tux.input.old_fire = tux.input.fire;
518 if(current_menu == game_menu)
520 switch (game_menu->check())
523 st_pause_ticks_stop();
526 update_load_save_game_menu(save_game_menu, false);
529 update_load_save_game_menu(load_game_menu, true);
532 st_pause_ticks_stop();
537 else if(current_menu == options_menu)
539 process_options_menu();
541 else if(current_menu == save_game_menu )
543 process_save_game_menu();
545 else if(current_menu == load_game_menu )
547 process_load_game_menu();
552 /* Handle actions: */
554 if(!game_pause && !show_menu)
556 /*float z = frame_ratio;
560 if (action(frame_ratio) == 0)
562 /* == 0: no more lives */
563 /* == -1: continues */
575 if(debug_mode && debug_fps)
578 /*Draw the current scene to the screen */
579 /*If the machine running the game is too slow
580 skip the drawing of the frame (so the calculations are more precise and
581 the FPS aren't affected).*/
582 /*if( ! fps_fps < 50.0 )
585 jump = true;*/ /*FIXME: Implement this tweak right.*/
588 /* Time stops in pause mode */
589 if(game_pause || show_menu )
594 /* Set the time of the last update and the time of the current update */
595 last_update_time = update_time;
596 update_time = st_get_ticks();
598 /* Pause till next frame, if the machine running the game is too fast: */
599 /* FIXME: Works great for in OpenGl mode, where the CPU doesn't have to do that much. But
600 the results in SDL mode aren't perfect (thought the 100 FPS are reached), even on an AMD2500+. */
601 if(last_update_time >= update_time - 12) {
603 update_time = st_get_ticks();
605 /*if((update_time - last_update_time) < 10)
606 SDL_Delay((11 - (update_time - last_update_time))/2);*/
609 if (time_left.check())
611 /* are we low on time ? */
612 if ((timer_get_left(&time_left) < TIME_WARNING)
613 && (get_current_music() != HURRYUP_MUSIC)) /* play the fast music */
615 set_current_music(HURRYUP_MUSIC);
616 play_current_music();
623 /* Calculate frames per second */
627 fps_fps = (1000.0 / (float)timer_get_gone(&fps_timer)) * (float)fps_cnt;
629 if(!fps_timer.check())
631 fps_timer.start(1000);
639 world->get_level()->free_gfx();
640 world->get_level()->cleanup();
641 world->get_level()->free_song();
644 world->arrays_free();
649 /* Bounce a brick: */
650 void bumpbrick(float x, float y)
652 World::current()->add_bouncy_brick(((int)(x + 1) / 32) * 32,
655 play_sound(sounds[SND_BRICK], SOUND_CENTER_SPEAKER);
660 GameSession::drawstatus()
662 Player& tux = *world->get_tux();
665 sprintf(str, "%d", player_status.score);
666 text_draw(&white_text, "SCORE", 0, 0, 1);
667 text_draw(&gold_text, str, 96, 0, 1);
669 if(st_gl_mode != ST_GL_TEST)
671 sprintf(str, "%d", hs_score);
672 text_draw(&white_text, "HIGH", 0, 20, 1);
673 text_draw(&gold_text, str, 96, 20, 1);
677 text_draw(&white_text,"Press ESC To Return",0,20,1);
680 if (timer_get_left(&time_left) > TIME_WARNING || (global_frame_counter % 10) < 5)
682 sprintf(str, "%d", timer_get_left(&time_left) / 1000 );
683 text_draw(&white_text, "TIME", 224, 0, 1);
684 text_draw(&gold_text, str, 304, 0, 1);
687 sprintf(str, "%d", player_status.distros);
688 text_draw(&white_text, "DISTROS", screen->h, 0, 1);
689 text_draw(&gold_text, str, 608, 0, 1);
691 text_draw(&white_text, "LIVES", screen->h, 20, 1);
695 sprintf(str, "%2.1f", fps_fps);
696 text_draw(&white_text, "FPS", screen->h, 40, 1);
697 text_draw(&gold_text, str, screen->h + 60, 40, 1);
700 for(int i= 0; i < tux.lives; ++i)
702 texture_draw(&tux_life,565+(18*i),20);
707 GameSession::drawendscreen()
711 clearscreen(0, 0, 0);
713 text_drawf(&blue_text, "GAMEOVER", 0, 200, A_HMIDDLE, A_TOP, 1);
715 sprintf(str, "SCORE: %d", player_status.score);
716 text_drawf(&gold_text, str, 0, 224, A_HMIDDLE, A_TOP, 1);
718 sprintf(str, "DISTROS: %d", player_status.distros);
719 text_drawf(&gold_text, str, 0, 256, A_HMIDDLE, A_TOP, 1);
724 wait_for_event(event,2000,5000,true);
728 GameSession::drawresultscreen(void)
732 clearscreen(0, 0, 0);
734 text_drawf(&blue_text, "Result:", 0, 200, A_HMIDDLE, A_TOP, 1);
736 sprintf(str, "SCORE: %d", player_status.score);
737 text_drawf(&gold_text, str, 0, 224, A_HMIDDLE, A_TOP, 1);
739 sprintf(str, "DISTROS: %d", player_status.distros);
740 text_drawf(&gold_text, str, 0, 256, A_HMIDDLE, A_TOP, 1);
745 wait_for_event(event,2000,5000,true);
749 GameSession::savegame(int)
756 sprintf(savefile,"%s/slot%d.save",st_save_dir,slot);
758 fi = fopen(savefile, "wb");
762 fprintf(stderr, "Warning: I could not open the slot file ");
766 fputs(level_subset, fi);
768 fwrite(&level,sizeof(int),1,fi);
769 fwrite(&score,sizeof(int),1,fi);
770 fwrite(&distros,sizeof(int),1,fi);
771 fwrite(&scroll_x,sizeof(float),1,fi);
772 //FIXME:fwrite(&tux,sizeof(Player),1,fi);
773 //FIXME:timer_fwrite(&tux.invincible_timer,fi);
774 //FIXME:timer_fwrite(&tux.skidding_timer,fi);
775 //FIXME:timer_fwrite(&tux.safe_timer,fi);
776 //FIXME:timer_fwrite(&tux.frame_timer,fi);
777 timer_fwrite(&time_left,fi);
779 fwrite(&ui,sizeof(int),1,fi);
786 GameSession::loadgame(int)
794 sprintf(savefile,"%s/slot%d.save",st_save_dir,slot);
796 fi = fopen(savefile, "rb");
800 fprintf(stderr, "Warning: I could not open the slot file ");
806 strcpy(level_subset, str);
807 level_subset[strlen(level_subset)-1] = '\0';
808 fread(&level,sizeof(int),1,fi);
810 world->set_defaults();
811 world->get_level()->cleanup();
812 world->arrays_free();
813 world->get_level()->free_gfx();
814 world->get_level()->free_song();
816 if(world->get_level()->load(level_subset,level) != 0)
819 world->activate_bad_guys();
820 world->activate_particle_systems();
821 world->get_level()->load_gfx();
822 world->get_level()->load_song();
825 update_time = st_get_ticks();
827 fread(&score, sizeof(int),1,fi);
828 fread(&distros, sizeof(int),1,fi);
829 fread(&scroll_x,sizeof(float),1,fi);
830 //FIXME:fread(&tux, sizeof(Player), 1, fi);
831 //FIXME:timer_fread(&tux.invincible_timer,fi);
832 //FIXME:timer_fread(&tux.skidding_timer,fi);
833 //FIXME:timer_fread(&tux.safe_timer,fi);
834 //FIXME:timer_fread(&tux.frame_timer,fi);
835 timer_fread(&time_left,fi);
836 fread(&ui,sizeof(int),1,fi);
842 std::string slotinfo(int slot)
849 sprintf(slotfile,"%s/slot%d.save",st_save_dir,slot);
851 fi = fopen(slotfile, "rb");
853 sprintf(tmp,"Slot %d - ",slot);
862 str[strlen(str)-1] = '\0';
864 strcat(tmp, " / Level:");
865 fread(&slot_level,sizeof(int),1,fi);
866 sprintf(str,"%d",slot_level);