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();
435 /* update particle systems */
436 std::vector<ParticleSystem*>::iterator p;
437 for(p = world->particle_systems.begin(); p != world->particle_systems.end(); ++p)
439 (*p)->simulate(frame_ratio);
442 /* Handle all possible collisions. */
456 int x = screen->h / 20;
457 for(int i = 0; i < x; ++i)
459 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);
461 fillrect(0,0,screen->w,screen->h,rand() % 50, rand() % 50, rand() % 50, 128);
462 text_drawf(&blue_text, "PAUSE - Press 'P' To Play", 0, 230, A_HMIDDLE, A_TOP, 1);
467 menu_process_current();
468 mouse_cursor->draw();
483 global_frame_counter = 0;
485 timer_init(&fps_timer,true);
486 timer_init(&frame_timer,true);
487 last_update_time = st_get_ticks();
491 clearscreen(0, 0, 0);
495 play_current_music();
497 while (SDL_PollEvent(&event))
504 while (!done && !quit)
506 /* Calculate the movement-factor */
507 frame_ratio = ((double)(update_time-last_update_time))/((double)FRAME_RATE);
508 if(frame_ratio > 1.5) /* Quick hack to correct the unprecise CPU clocks a little bit. */
509 frame_ratio = 1.5 + (frame_ratio - 1.5) * 0.85;
511 if(!timer_check(&frame_timer))
513 timer_start(&frame_timer,25);
514 ++global_frame_counter;
519 tux.input.old_fire = tux.input.fire;
525 if(current_menu == game_menu)
527 switch (game_menu->check())
530 st_pause_ticks_stop();
533 update_load_save_game_menu(save_game_menu, false);
536 update_load_save_game_menu(load_game_menu, true);
539 st_pause_ticks_stop();
544 else if(current_menu == options_menu)
546 process_options_menu();
548 else if(current_menu == save_game_menu )
550 process_save_game_menu();
552 else if(current_menu == load_game_menu )
554 process_load_game_menu();
559 /* Handle actions: */
561 if(!game_pause && !show_menu)
563 /*float z = frame_ratio;
569 /* == 0: no more lives */
570 /* == -1: continues */
582 if(debug_mode && debug_fps)
585 /*Draw the current scene to the screen */
586 /*If the machine running the game is too slow
587 skip the drawing of the frame (so the calculations are more precise and
588 the FPS aren't affected).*/
589 /*if( ! fps_fps < 50.0 )
592 jump = true;*/ /*FIXME: Implement this tweak right.*/
595 /* Time stops in pause mode */
596 if(game_pause || show_menu )
601 /* Set the time of the last update and the time of the current update */
602 last_update_time = update_time;
603 update_time = st_get_ticks();
605 /* Pause till next frame, if the machine running the game is too fast: */
606 /* FIXME: Works great for in OpenGl mode, where the CPU doesn't have to do that much. But
607 the results in SDL mode aren't perfect (thought the 100 FPS are reached), even on an AMD2500+. */
608 if(last_update_time >= update_time - 12) {
610 update_time = st_get_ticks();
612 /*if((update_time - last_update_time) < 10)
613 SDL_Delay((11 - (update_time - last_update_time))/2);*/
616 if (timer_check(&time_left))
618 /* are we low on time ? */
619 if ((timer_get_left(&time_left) < TIME_WARNING)
620 && (get_current_music() != HURRYUP_MUSIC)) /* play the fast music */
622 set_current_music(HURRYUP_MUSIC);
623 play_current_music();
630 /* Calculate frames per second */
634 fps_fps = (1000.0 / (float)timer_get_gone(&fps_timer)) * (float)fps_cnt;
636 if(!timer_check(&fps_timer))
638 timer_start(&fps_timer,1000);
646 world->get_level()->free_gfx();
647 world->get_level()->cleanup();
648 world->get_level()->free_song();
651 world->arrays_free();
653 game_started = false;
658 /* Bounce a brick: */
659 void bumpbrick(float x, float y)
661 world.add_bouncy_brick(((int)(x + 1) / 32) * 32,
664 play_sound(sounds[SND_BRICK], SOUND_CENTER_SPEAKER);
668 void drawstatus(void)
672 sprintf(str, "%d", score);
673 text_draw(&white_text, "SCORE", 0, 0, 1);
674 text_draw(&gold_text, str, 96, 0, 1);
676 if(st_gl_mode != ST_GL_TEST)
678 sprintf(str, "%d", hs_score);
679 text_draw(&white_text, "HIGH", 0, 20, 1);
680 text_draw(&gold_text, str, 96, 20, 1);
684 text_draw(&white_text,"Press ESC To Return",0,20,1);
687 if (timer_get_left(&time_left) > TIME_WARNING || (global_frame_counter % 10) < 5)
689 sprintf(str, "%d", timer_get_left(&time_left) / 1000 );
690 text_draw(&white_text, "TIME", 224, 0, 1);
691 text_draw(&gold_text, str, 304, 0, 1);
694 sprintf(str, "%d", distros);
695 text_draw(&white_text, "DISTROS", screen->h, 0, 1);
696 text_draw(&gold_text, str, 608, 0, 1);
698 text_draw(&white_text, "LIVES", screen->h, 20, 1);
702 sprintf(str, "%2.1f", fps_fps);
703 text_draw(&white_text, "FPS", screen->h, 40, 1);
704 text_draw(&gold_text, str, screen->h + 60, 40, 1);
707 for(int i=0; i < tux.lives; ++i)
709 texture_draw(&tux_life,565+(18*i),20);
714 void drawendscreen(void)
718 clearscreen(0, 0, 0);
720 text_drawf(&blue_text, "GAMEOVER", 0, 200, A_HMIDDLE, A_TOP, 1);
722 sprintf(str, "SCORE: %d", score);
723 text_drawf(&gold_text, str, 0, 224, A_HMIDDLE, A_TOP, 1);
725 sprintf(str, "DISTROS: %d", distros);
726 text_drawf(&gold_text, str, 0, 256, A_HMIDDLE, A_TOP, 1);
731 wait_for_event(event,2000,5000,true);
734 void drawresultscreen(void)
738 clearscreen(0, 0, 0);
740 text_drawf(&blue_text, "Result:", 0, 200, A_HMIDDLE, A_TOP, 1);
742 sprintf(str, "SCORE: %d", score);
743 text_drawf(&gold_text, str, 0, 224, A_HMIDDLE, A_TOP, 1);
745 sprintf(str, "DISTROS: %d", distros);
746 text_drawf(&gold_text, str, 0, 256, A_HMIDDLE, A_TOP, 1);
751 wait_for_event(event,2000,5000,true);
755 GameSession::savegame(int slot)
761 sprintf(savefile,"%s/slot%d.save",st_save_dir,slot);
763 fi = fopen(savefile, "wb");
767 fprintf(stderr, "Warning: I could not open the slot file ");
771 fputs(level_subset, fi);
773 fwrite(&level,sizeof(int),1,fi);
774 fwrite(&score,sizeof(int),1,fi);
775 fwrite(&distros,sizeof(int),1,fi);
776 fwrite(&scroll_x,sizeof(float),1,fi);
777 fwrite(&tux,sizeof(Player),1,fi);
778 timer_fwrite(&tux.invincible_timer,fi);
779 timer_fwrite(&tux.skidding_timer,fi);
780 timer_fwrite(&tux.safe_timer,fi);
781 timer_fwrite(&tux.frame_timer,fi);
782 timer_fwrite(&time_left,fi);
784 fwrite(&ui,sizeof(int),1,fi);
791 GameSession::loadgame(int slot)
798 sprintf(savefile,"%s/slot%d.save",st_save_dir,slot);
800 fi = fopen(savefile, "rb");
804 fprintf(stderr, "Warning: I could not open the slot file ");
810 strcpy(level_subset, str);
811 level_subset[strlen(level_subset)-1] = '\0';
812 fread(&level,sizeof(int),1,fi);
814 world->set_defaults();
815 world->get_level()->cleanup();
816 world->arrays_free();
817 world->get_level()->free_gfx();
818 world->get_level()->free_song();
820 if(world->get_level()->load(level_subset,level) != 0)
823 world->activate_bad_guys();
824 world->activate_particle_systems();
825 world->get_level()->load_gfx();
826 world->get_level()->load_song();
829 update_time = st_get_ticks();
831 fread(&score, sizeof(int),1,fi);
832 fread(&distros, sizeof(int),1,fi);
833 fread(&scroll_x,sizeof(float),1,fi);
834 fread(&tux, sizeof(Player), 1, fi);
835 timer_fread(&tux.invincible_timer,fi);
836 timer_fread(&tux.skidding_timer,fi);
837 timer_fread(&tux.safe_timer,fi);
838 timer_fread(&tux.frame_timer,fi);
839 timer_fread(&time_left,fi);
840 fread(&ui,sizeof(int),1,fi);
846 std::string slotinfo(int slot)
853 sprintf(slotfile,"%s/slot%d.save",st_save_dir,slot);
855 fi = fopen(slotfile, "rb");
857 sprintf(tmp,"Slot %d - ",slot);
866 str[strlen(str)-1] = '\0';
868 strcat(tmp, " / Level:");
869 fread(&slot_level,sizeof(int),1,fi);
870 sprintf(str,"%d",slot_level);