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();
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... */
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 /* Draw a tile on the screen: */
660 void drawshape(float x, float y, unsigned int c, Uint8 alpha)
664 Tile* ptile = TileManager::instance()->get(c);
667 if(ptile->images.size() > 1)
669 texture_draw(&ptile->images[( ((global_frame_counter*25) / ptile->anim_speed) % (ptile->images.size()))],x,y, alpha);
671 else if (ptile->images.size() == 1)
673 texture_draw(&ptile->images[0],x,y, alpha);
677 //printf("Tile not dravable %u\n", c);
683 /* Bounce a brick: */
684 void bumpbrick(float x, float y)
686 world.add_bouncy_brick(((int)(x + 1) / 32) * 32,
689 play_sound(sounds[SND_BRICK], SOUND_CENTER_SPEAKER);
693 void drawstatus(void)
697 sprintf(str, "%d", score);
698 text_draw(&white_text, "SCORE", 0, 0, 1);
699 text_draw(&gold_text, str, 96, 0, 1);
701 if(st_gl_mode != ST_GL_TEST)
703 sprintf(str, "%d", hs_score);
704 text_draw(&white_text, "HIGH", 0, 20, 1);
705 text_draw(&gold_text, str, 96, 20, 1);
709 text_draw(&white_text,"Press ESC To Return",0,20,1);
712 if (timer_get_left(&time_left) > TIME_WARNING || (global_frame_counter % 10) < 5)
714 sprintf(str, "%d", timer_get_left(&time_left) / 1000 );
715 text_draw(&white_text, "TIME", 224, 0, 1);
716 text_draw(&gold_text, str, 304, 0, 1);
719 sprintf(str, "%d", distros);
720 text_draw(&white_text, "DISTROS", screen->h, 0, 1);
721 text_draw(&gold_text, str, 608, 0, 1);
723 text_draw(&white_text, "LIVES", screen->h, 20, 1);
727 sprintf(str, "%2.1f", fps_fps);
728 text_draw(&white_text, "FPS", screen->h, 40, 1);
729 text_draw(&gold_text, str, screen->h + 60, 40, 1);
732 for(int i=0; i < tux.lives; ++i)
734 texture_draw(&tux_life,565+(18*i),20);
739 void drawendscreen(void)
743 clearscreen(0, 0, 0);
745 text_drawf(&blue_text, "GAMEOVER", 0, 200, A_HMIDDLE, A_TOP, 1);
747 sprintf(str, "SCORE: %d", score);
748 text_drawf(&gold_text, str, 0, 224, A_HMIDDLE, A_TOP, 1);
750 sprintf(str, "DISTROS: %d", distros);
751 text_drawf(&gold_text, str, 0, 256, A_HMIDDLE, A_TOP, 1);
756 wait_for_event(event,2000,5000,true);
759 void drawresultscreen(void)
763 clearscreen(0, 0, 0);
765 text_drawf(&blue_text, "Result:", 0, 200, A_HMIDDLE, A_TOP, 1);
767 sprintf(str, "SCORE: %d", score);
768 text_drawf(&gold_text, str, 0, 224, A_HMIDDLE, A_TOP, 1);
770 sprintf(str, "DISTROS: %d", distros);
771 text_drawf(&gold_text, str, 0, 256, A_HMIDDLE, A_TOP, 1);
776 wait_for_event(event,2000,5000,true);
780 GameSession::savegame(int slot)
786 sprintf(savefile,"%s/slot%d.save",st_save_dir,slot);
788 fi = fopen(savefile, "wb");
792 fprintf(stderr, "Warning: I could not open the slot file ");
796 fputs(level_subset, fi);
798 fwrite(&level,sizeof(int),1,fi);
799 fwrite(&score,sizeof(int),1,fi);
800 fwrite(&distros,sizeof(int),1,fi);
801 fwrite(&scroll_x,sizeof(float),1,fi);
802 fwrite(&tux,sizeof(Player),1,fi);
803 timer_fwrite(&tux.invincible_timer,fi);
804 timer_fwrite(&tux.skidding_timer,fi);
805 timer_fwrite(&tux.safe_timer,fi);
806 timer_fwrite(&tux.frame_timer,fi);
807 timer_fwrite(&time_left,fi);
809 fwrite(&ui,sizeof(int),1,fi);
816 GameSession::loadgame(int slot)
823 sprintf(savefile,"%s/slot%d.save",st_save_dir,slot);
825 fi = fopen(savefile, "rb");
829 fprintf(stderr, "Warning: I could not open the slot file ");
835 strcpy(level_subset, str);
836 level_subset[strlen(level_subset)-1] = '\0';
837 fread(&level,sizeof(int),1,fi);
840 world->get_level()->cleanup();
841 world->arrays_free();
843 if(world->get_level()->load(level_subset,level) != 0)
845 world->activate_bad_guys();
846 world->activate_particle_systems();
848 world->get_level()->free_gfx();
849 world->get_level()->load_gfx();
850 world->get_level()->free_song();
851 world->get_level()->load_song();
854 update_time = st_get_ticks();
856 fread(&score, sizeof(int),1,fi);
857 fread(&distros, sizeof(int),1,fi);
858 fread(&scroll_x,sizeof(float),1,fi);
859 fread(&tux, sizeof(Player), 1, fi);
860 timer_fread(&tux.invincible_timer,fi);
861 timer_fread(&tux.skidding_timer,fi);
862 timer_fread(&tux.safe_timer,fi);
863 timer_fread(&tux.frame_timer,fi);
864 timer_fread(&time_left,fi);
865 fread(&ui,sizeof(int),1,fi);
871 std::string slotinfo(int slot)
878 sprintf(slotfile,"%s/slot%d.save",st_save_dir,slot);
880 fi = fopen(slotfile, "rb");
882 sprintf(tmp,"Slot %d - ",slot);
891 str[strlen(str)-1] = '\0';
893 strcat(tmp, " / Level:");
894 fread(&slot_level,sizeof(int),1,fi);
895 sprintf(str,"%d",slot_level);