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"
46 /* extern variables */
48 int game_started = false;
50 /* Local variables: */
51 static SDL_Event event;
53 static char level_subset[100];
55 static int st_gl_mode;
56 static unsigned int last_update_time;
57 static unsigned int update_time;
58 static int pause_menu_frame;
61 GameSession* GameSession::current_ = 0;
63 /* Local function prototypes: */
64 void levelintro(void);
65 void loadshared(void);
66 void unloadshared(void);
67 void drawstatus(void);
68 void drawendscreen(void);
69 void drawresultscreen(void);
71 GameSession::GameSession()
77 GameSession::GameSession(const std::string& filename)
83 timer_init(&fps_timer, true);
84 timer_init(&frame_timer, true);
86 world->load(filename);
89 GameSession::GameSession(const std::string& subset, int levelnb, int mode)
95 timer_init(&fps_timer, true);
96 timer_init(&frame_timer, true);
104 world->arrays_free();
105 world->set_defaults();
107 strcpy(level_subset, subset.c_str());
109 if (st_gl_mode == ST_GL_LOAD_LEVEL_FILE)
111 if (world->load(level_subset))
116 if(world->load(level_subset, level) != 0)
120 world->get_level()->load_gfx();
123 world->activate_bad_guys();
124 world->activate_particle_systems();
125 world->get_level()->load_song();
129 if(st_gl_mode != ST_GL_TEST)
132 if(st_gl_mode == ST_GL_PLAY || st_gl_mode == ST_GL_LOAD_LEVEL_FILE)
135 timer_init(&time_left,true);
138 if(st_gl_mode == ST_GL_LOAD_GAME)
143 GameSession::levelintro(void)
147 clearscreen(0, 0, 0);
149 sprintf(str, "LEVEL %d", level);
150 text_drawf(&blue_text, str, 0, 200, A_HMIDDLE, A_TOP, 1);
152 sprintf(str, "%s", world->get_level()->name.c_str());
153 text_drawf(&gold_text, str, 0, 224, A_HMIDDLE, A_TOP, 1);
155 sprintf(str, "TUX x %d", tux.lives);
156 text_drawf(&white_text, str, 0, 256, A_HMIDDLE, A_TOP, 1);
161 wait_for_event(event,1000,3000,true);
166 GameSession::start_timers()
168 timer_start(&time_left, world->get_level()->time_left*1000);
169 st_pause_ticks_init();
170 update_time = st_get_ticks();
174 GameSession::process_events()
176 while (SDL_PollEvent(&event))
178 /* Check for menu-events, if the menu is shown */
184 case SDL_QUIT: /* Quit event - quit: */
187 case SDL_KEYDOWN: /* A keypress! */
188 key = event.key.keysym.sym;
190 if(tux.key_event(key,DOWN))
195 case SDLK_ESCAPE: /* Escape: Open/Close the menu: */
198 if(st_gl_mode == ST_GL_TEST)
202 Menu::set_current(game_menu);
204 st_pause_ticks_stop();
208 Menu::set_current(game_menu);
210 st_pause_ticks_start();
218 case SDL_KEYUP: /* A keyrelease! */
219 key = event.key.keysym.sym;
221 if(tux.key_event(key, UP))
232 st_pause_ticks_stop();
237 st_pause_ticks_start();
244 tux.size = !tux.size;
247 tux.base.height = 64;
250 tux.base.height = 32;
267 timer_start(&tux.invincible_timer,TUX_INVINCIBLE_TIME);
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()
345 if (tux.is_dead() || next_level)
347 /* Tux either died, or reached the end of a level! */
352 /* End of a level! */
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 (score > hs_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(level_subset) != 0)
411 if(world->get_level()->load(level_subset,level) != 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();
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();
473 global_frame_counter = 0;
475 timer_init(&fps_timer,true);
476 timer_init(&frame_timer,true);
477 last_update_time = st_get_ticks();
481 clearscreen(0, 0, 0);
485 play_current_music();
487 while (SDL_PollEvent(&event))
494 while (!done && !quit)
496 /* Calculate the movement-factor */
497 frame_ratio = ((double)(update_time-last_update_time))/((double)FRAME_RATE);
498 if(frame_ratio > 1.5) /* Quick hack to correct the unprecise CPU clocks a little bit. */
499 frame_ratio = 1.5 + (frame_ratio - 1.5) * 0.85;
501 if(!timer_check(&frame_timer))
503 timer_start(&frame_timer,25);
504 ++global_frame_counter;
509 tux.input.old_fire = tux.input.fire;
515 if(current_menu == game_menu)
517 switch (game_menu->check())
520 st_pause_ticks_stop();
523 update_load_save_game_menu(save_game_menu, false);
526 update_load_save_game_menu(load_game_menu, true);
529 st_pause_ticks_stop();
534 else if(current_menu == options_menu)
536 process_options_menu();
538 else if(current_menu == save_game_menu )
540 process_save_game_menu();
542 else if(current_menu == load_game_menu )
544 process_load_game_menu();
549 /* Handle actions: */
551 if(!game_pause && !show_menu)
553 /*float z = frame_ratio;
559 /* == 0: no more lives */
560 /* == -1: continues */
572 if(debug_mode && debug_fps)
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 )
582 jump = true;*/ /*FIXME: Implement this tweak right.*/
585 /* Time stops in pause mode */
586 if(game_pause || show_menu )
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();
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) {
600 update_time = st_get_ticks();
602 /*if((update_time - last_update_time) < 10)
603 SDL_Delay((11 - (update_time - last_update_time))/2);*/
606 if (timer_check(&time_left))
608 /* are we low on time ? */
609 if ((timer_get_left(&time_left) < TIME_WARNING)
610 && (get_current_music() != HURRYUP_MUSIC)) /* play the fast music */
612 set_current_music(HURRYUP_MUSIC);
613 play_current_music();
620 /* Calculate frames per second */
624 fps_fps = (1000.0 / (float)timer_get_gone(&fps_timer)) * (float)fps_cnt;
626 if(!timer_check(&fps_timer))
628 timer_start(&fps_timer,1000);
636 world->get_level()->free_gfx();
637 world->get_level()->cleanup();
638 world->get_level()->free_song();
641 world->arrays_free();
643 game_started = false;
648 /* Bounce a brick: */
649 void bumpbrick(float x, float y)
651 world.add_bouncy_brick(((int)(x + 1) / 32) * 32,
654 play_sound(sounds[SND_BRICK], SOUND_CENTER_SPEAKER);
658 void drawstatus(void)
662 sprintf(str, "%d", score);
663 text_draw(&white_text, "SCORE", 0, 0, 1);
664 text_draw(&gold_text, str, 96, 0, 1);
666 if(st_gl_mode != ST_GL_TEST)
668 sprintf(str, "%d", hs_score);
669 text_draw(&white_text, "HIGH", 0, 20, 1);
670 text_draw(&gold_text, str, 96, 20, 1);
674 text_draw(&white_text,"Press ESC To Return",0,20,1);
677 if (timer_get_left(&time_left) > TIME_WARNING || (global_frame_counter % 10) < 5)
679 sprintf(str, "%d", timer_get_left(&time_left) / 1000 );
680 text_draw(&white_text, "TIME", 224, 0, 1);
681 text_draw(&gold_text, str, 304, 0, 1);
684 sprintf(str, "%d", distros);
685 text_draw(&white_text, "DISTROS", screen->h, 0, 1);
686 text_draw(&gold_text, str, 608, 0, 1);
688 text_draw(&white_text, "LIVES", screen->h, 20, 1);
692 sprintf(str, "%2.1f", fps_fps);
693 text_draw(&white_text, "FPS", screen->h, 40, 1);
694 text_draw(&gold_text, str, screen->h + 60, 40, 1);
697 for(int i=0; i < tux.lives; ++i)
699 texture_draw(&tux_life,565+(18*i),20);
704 void drawendscreen(void)
708 clearscreen(0, 0, 0);
710 text_drawf(&blue_text, "GAMEOVER", 0, 200, A_HMIDDLE, A_TOP, 1);
712 sprintf(str, "SCORE: %d", score);
713 text_drawf(&gold_text, str, 0, 224, A_HMIDDLE, A_TOP, 1);
715 sprintf(str, "DISTROS: %d", distros);
716 text_drawf(&gold_text, str, 0, 256, A_HMIDDLE, A_TOP, 1);
721 wait_for_event(event,2000,5000,true);
724 void drawresultscreen(void)
728 clearscreen(0, 0, 0);
730 text_drawf(&blue_text, "Result:", 0, 200, A_HMIDDLE, A_TOP, 1);
732 sprintf(str, "SCORE: %d", score);
733 text_drawf(&gold_text, str, 0, 224, A_HMIDDLE, A_TOP, 1);
735 sprintf(str, "DISTROS: %d", distros);
736 text_drawf(&gold_text, str, 0, 256, A_HMIDDLE, A_TOP, 1);
741 wait_for_event(event,2000,5000,true);
745 GameSession::savegame(int slot)
751 sprintf(savefile,"%s/slot%d.save",st_save_dir,slot);
753 fi = fopen(savefile, "wb");
757 fprintf(stderr, "Warning: I could not open the slot file ");
761 fputs(level_subset, fi);
763 fwrite(&level,sizeof(int),1,fi);
764 fwrite(&score,sizeof(int),1,fi);
765 fwrite(&distros,sizeof(int),1,fi);
766 fwrite(&scroll_x,sizeof(float),1,fi);
767 fwrite(&tux,sizeof(Player),1,fi);
768 timer_fwrite(&tux.invincible_timer,fi);
769 timer_fwrite(&tux.skidding_timer,fi);
770 timer_fwrite(&tux.safe_timer,fi);
771 timer_fwrite(&tux.frame_timer,fi);
772 timer_fwrite(&time_left,fi);
774 fwrite(&ui,sizeof(int),1,fi);
781 GameSession::loadgame(int slot)
788 sprintf(savefile,"%s/slot%d.save",st_save_dir,slot);
790 fi = fopen(savefile, "rb");
794 fprintf(stderr, "Warning: I could not open the slot file ");
800 strcpy(level_subset, str);
801 level_subset[strlen(level_subset)-1] = '\0';
802 fread(&level,sizeof(int),1,fi);
804 world->set_defaults();
805 world->get_level()->cleanup();
806 world->arrays_free();
807 world->get_level()->free_gfx();
808 world->get_level()->free_song();
810 if(world->get_level()->load(level_subset,level) != 0)
813 world->activate_bad_guys();
814 world->activate_particle_systems();
815 world->get_level()->load_gfx();
816 world->get_level()->load_song();
819 update_time = st_get_ticks();
821 fread(&score, sizeof(int),1,fi);
822 fread(&distros, sizeof(int),1,fi);
823 fread(&scroll_x,sizeof(float),1,fi);
824 fread(&tux, sizeof(Player), 1, fi);
825 timer_fread(&tux.invincible_timer,fi);
826 timer_fread(&tux.skidding_timer,fi);
827 timer_fread(&tux.safe_timer,fi);
828 timer_fread(&tux.frame_timer,fi);
829 timer_fread(&time_left,fi);
830 fread(&ui,sizeof(int),1,fi);
836 std::string slotinfo(int slot)
843 sprintf(slotfile,"%s/slot%d.save",st_save_dir,slot);
845 fi = fopen(slotfile, "rb");
847 sprintf(tmp,"Slot %d - ",slot);
856 str[strlen(str)-1] = '\0';
858 strcat(tmp, " / Level:");
859 fread(&slot_level,sizeof(int),1,fi);
860 sprintf(str,"%d",slot_level);