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");
71 frame_timer.init(true);
73 world->load(filename);
76 GameSession::GameSession(const std::string& subset_, int levelnb_, int mode)
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();
108 world->activate_bad_guys();
109 world->activate_particle_systems();
110 world->get_level()->load_song();
112 if(st_gl_mode != ST_GL_TEST)
115 if(st_gl_mode == ST_GL_PLAY || st_gl_mode == ST_GL_LOAD_LEVEL_FILE)
118 time_left.init(true);
121 if(st_gl_mode == ST_GL_LOAD_GAME)
125 GameSession::~GameSession()
131 GameSession::levelintro(void)
133 Player& tux = *world->get_tux();
137 clearscreen(0, 0, 0);
139 sprintf(str, "LEVEL %d", levelnb);
140 blue_text->drawf(str, 0, 200, A_HMIDDLE, A_TOP, 1);
142 sprintf(str, "%s", world->get_level()->name.c_str());
143 gold_text->drawf(str, 0, 224, A_HMIDDLE, A_TOP, 1);
145 sprintf(str, "by %s", world->get_level()->author.c_str());
146 red_text->drawf(str, 0, 256, A_HMIDDLE, A_TOP, 1);
148 sprintf(str, "TUX x %d", tux.lives);
149 white_text->drawf(str, 0, 288, A_HMIDDLE, A_TOP, 1);
154 wait_for_event(event,1000,3000,true);
159 GameSession::start_timers()
161 time_left.start(world->get_level()->time_left*1000);
162 st_pause_ticks_init();
163 update_time = st_get_ticks();
167 GameSession::process_events()
169 Player& tux = *world->get_tux();
172 while (SDL_PollEvent(&event))
174 /* Check for menu-events, if the menu is shown */
176 current_menu->event(event);
180 case SDL_QUIT: /* Quit event - quit: */
183 case SDL_KEYDOWN: /* A keypress! */
185 SDLKey key = event.key.keysym.sym;
187 if(tux.key_event(key,DOWN))
192 case SDLK_ESCAPE: /* Escape: Open/Close the menu: */
195 if(st_gl_mode == ST_GL_TEST)
199 Menu::set_current(game_menu);
201 st_pause_ticks_stop();
205 Menu::set_current(game_menu);
207 st_pause_ticks_start();
216 case SDL_KEYUP: /* A keyrelease! */
218 SDLKey key = event.key.keysym.sym;
220 if(tux.key_event(key, UP))
231 st_pause_ticks_stop();
236 st_pause_ticks_start();
243 tux.size = !tux.size;
246 tux.base.height = 64;
249 tux.base.height = 32;
254 player_status.distros += 50;
258 player_status.next_level = 1;
266 tux.invincible_timer.start(TUX_INVINCIBLE_TIME);
274 player_status.score += 1000;
287 case SDL_JOYAXISMOTION:
288 switch(event.jaxis.axis)
291 if (event.jaxis.value < -JOYSTICK_DEAD_ZONE)
293 tux.input.left = DOWN;
294 tux.input.right = UP;
296 else if (event.jaxis.value > JOYSTICK_DEAD_ZONE)
299 tux.input.right = DOWN;
303 tux.input.left = DOWN;
304 tux.input.right = DOWN;
308 if (event.jaxis.value > JOYSTICK_DEAD_ZONE)
309 tux.input.down = DOWN;
310 else if (event.jaxis.value < -JOYSTICK_DEAD_ZONE)
320 case SDL_JOYBUTTONDOWN:
321 if (event.jbutton.button == JOY_A)
323 else if (event.jbutton.button == JOY_B)
324 tux.input.fire = DOWN;
326 case SDL_JOYBUTTONUP:
327 if (event.jbutton.button == JOY_A)
329 else if (event.jbutton.button == JOY_B)
343 GameSession::action(double frame_ratio)
345 Player& tux = *world->get_tux();
347 if (tux.is_dead() || player_status.next_level)
349 /* Tux either died, or reached the end of a level! */
352 if (player_status.next_level)
354 /* End of a level! */
356 player_status.next_level = 0;
357 if(st_gl_mode != ST_GL_TEST)
363 world->get_level()->free_gfx();
364 world->get_level()->cleanup();
365 world->get_level()->free_song();
366 world->arrays_free();
376 /* No more lives!? */
380 if(st_gl_mode != ST_GL_TEST)
383 if(st_gl_mode != ST_GL_TEST)
385 if (player_status.score > hs_score)
386 save_hs(player_status.score);
389 world->get_level()->free_gfx();
390 world->get_level()->cleanup();
391 world->get_level()->free_song();
392 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 blue_text->drawf("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 (time_left.get_left() < TIME_WARNING
613 && (get_current_music() != HURRYUP_MUSIC)) /* play the fast music */
615 set_current_music(HURRYUP_MUSIC);
616 play_current_music();
620 else if(tux.dying == DYING_NOT)
623 /* Calculate frames per second */
627 fps_fps = (1000.0 / (float)fps_timer.get_gone()) * (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();
643 world->arrays_free();
648 /* Bounce a brick: */
649 void bumpbrick(float x, float y)
651 World::current()->add_bouncy_brick(((int)(x + 1) / 32) * 32,
654 play_sound(sounds[SND_BRICK], SOUND_CENTER_SPEAKER);
659 GameSession::drawstatus()
661 Player& tux = *world->get_tux();
664 sprintf(str, "%d", player_status.score);
665 white_text->draw("SCORE", 0, 0, 1);
666 gold_text->draw(str, 96, 0, 1);
668 if(st_gl_mode != ST_GL_TEST)
670 sprintf(str, "%d", hs_score);
671 white_text->draw("HIGH", 0, 20, 1);
672 gold_text->draw(str, 96, 20, 1);
676 white_text->draw("Press ESC To Return",0,20,1);
679 if (time_left.get_left() > TIME_WARNING || (global_frame_counter % 10) < 5)
681 sprintf(str, "%d", time_left.get_left() / 1000 );
682 white_text->draw("TIME", 224, 0, 1);
683 gold_text->draw(str, 304, 0, 1);
686 sprintf(str, "%d", player_status.distros);
687 white_text->draw("DISTROS", screen->h, 0, 1);
688 gold_text->draw(str, 608, 0, 1);
690 white_text->draw("LIVES", screen->h, 20, 1);
694 sprintf(str, "%2.1f", fps_fps);
695 white_text->draw("FPS", screen->h, 40, 1);
696 gold_text->draw(str, screen->h + 60, 40, 1);
699 for(int i= 0; i < tux.lives; ++i)
701 tux_life->draw(565+(18*i),20);
706 GameSession::drawendscreen()
710 clearscreen(0, 0, 0);
712 blue_text->drawf("GAMEOVER", 0, 200, A_HMIDDLE, A_TOP, 1);
714 sprintf(str, "SCORE: %d", player_status.score);
715 gold_text->drawf(str, 0, 224, A_HMIDDLE, A_TOP, 1);
717 sprintf(str, "DISTROS: %d", player_status.distros);
718 gold_text->drawf(str, 0, 256, A_HMIDDLE, A_TOP, 1);
723 wait_for_event(event,2000,5000,true);
727 GameSession::drawresultscreen(void)
731 clearscreen(0, 0, 0);
733 blue_text->drawf("Result:", 0, 200, A_HMIDDLE, A_TOP, 1);
735 sprintf(str, "SCORE: %d", player_status.score);
736 gold_text->drawf(str, 0, 224, A_HMIDDLE, A_TOP, 1);
738 sprintf(str, "DISTROS: %d", player_status.distros);
739 gold_text->drawf(str, 0, 256, A_HMIDDLE, A_TOP, 1);
744 wait_for_event(event,2000,5000,true);
748 GameSession::savegame(int)
755 sprintf(savefile,"%s/slot%d.save",st_save_dir,slot);
757 fi = fopen(savefile, "wb");
761 fprintf(stderr, "Warning: I could not open the slot file ");
765 fputs(level_subset, fi);
767 fwrite(&level,sizeof(int),1,fi);
768 fwrite(&score,sizeof(int),1,fi);
769 fwrite(&distros,sizeof(int),1,fi);
770 fwrite(&scroll_x,sizeof(float),1,fi);
771 //FIXME:fwrite(&tux,sizeof(Player),1,fi);
772 //FIXME:timer_fwrite(&tux.invincible_timer,fi);
773 //FIXME:timer_fwrite(&tux.skidding_timer,fi);
774 //FIXME:timer_fwrite(&tux.safe_timer,fi);
775 //FIXME:timer_fwrite(&tux.frame_timer,fi);
776 timer_fwrite(&time_left,fi);
778 fwrite(&ui,sizeof(int),1,fi);
785 GameSession::loadgame(int)
793 sprintf(savefile,"%s/slot%d.save",st_save_dir,slot);
795 fi = fopen(savefile, "rb");
799 fprintf(stderr, "Warning: I could not open the slot file ");
805 strcpy(level_subset, str);
806 level_subset[strlen(level_subset)-1] = '\0';
807 fread(&level,sizeof(int),1,fi);
809 world->set_defaults();
810 world->get_level()->cleanup();
811 world->arrays_free();
812 world->get_level()->free_gfx();
813 world->get_level()->free_song();
815 if(world->get_level()->load(level_subset,level) != 0)
818 world->activate_bad_guys();
819 world->activate_particle_systems();
820 world->get_level()->load_gfx();
821 world->get_level()->load_song();
824 update_time = st_get_ticks();
826 fread(&score, sizeof(int),1,fi);
827 fread(&distros, sizeof(int),1,fi);
828 fread(&scroll_x,sizeof(float),1,fi);
829 //FIXME:fread(&tux, sizeof(Player), 1, fi);
830 //FIXME:timer_fread(&tux.invincible_timer,fi);
831 //FIXME:timer_fread(&tux.skidding_timer,fi);
832 //FIXME:timer_fread(&tux.safe_timer,fi);
833 //FIXME:timer_fread(&tux.frame_timer,fi);
834 timer_fread(&time_left,fi);
835 fread(&ui,sizeof(int),1,fi);
841 std::string slotinfo(int slot)
848 sprintf(slotfile,"%s/slot%d.save",st_save_dir,slot);
850 fi = fopen(slotfile, "rb");
852 sprintf(tmp,"Slot %d - ",slot);
861 str[strlen(str)-1] = '\0';
863 strcat(tmp, " / Level:");
864 fread(&slot_level,sizeof(int),1,fi);
865 sprintf(str,"%d",slot_level);