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();
122 activate_bad_guys(world->get_level());
123 world->activate_particle_systems();
124 world->get_level()->load_song();
128 if(st_gl_mode != ST_GL_TEST)
131 if(st_gl_mode == ST_GL_PLAY || st_gl_mode == ST_GL_LOAD_LEVEL_FILE)
134 timer_init(&time_left,true);
137 if(st_gl_mode == ST_GL_LOAD_GAME)
142 GameSession::levelintro(void)
146 clearscreen(0, 0, 0);
148 sprintf(str, "LEVEL %d", level);
149 text_drawf(&blue_text, str, 0, 200, A_HMIDDLE, A_TOP, 1);
151 sprintf(str, "%s", world->get_level()->name.c_str());
152 text_drawf(&gold_text, str, 0, 224, A_HMIDDLE, A_TOP, 1);
154 sprintf(str, "TUX x %d", tux.lives);
155 text_drawf(&white_text, str, 0, 256, A_HMIDDLE, A_TOP, 1);
160 wait_for_event(event,1000,3000,true);
165 GameSession::start_timers()
167 timer_start(&time_left, world->get_level()->time_left*1000);
168 st_pause_ticks_init();
169 update_time = st_get_ticks();
172 void activate_bad_guys(Level* plevel)
174 for (std::vector<BadGuyData>::iterator i = plevel->badguy_data.begin();
175 i != plevel->badguy_data.end();
178 world.add_bad_guy(i->x, i->y, i->kind);
183 GameSession::process_events()
185 while (SDL_PollEvent(&event))
187 /* Check for menu-events, if the menu is shown */
193 case SDL_QUIT: /* Quit event - quit: */
196 case SDL_KEYDOWN: /* A keypress! */
197 key = event.key.keysym.sym;
199 if(tux.key_event(key,DOWN))
204 case SDLK_ESCAPE: /* Escape: Open/Close the menu: */
207 if(st_gl_mode == ST_GL_TEST)
211 Menu::set_current(game_menu);
213 st_pause_ticks_stop();
217 Menu::set_current(game_menu);
219 st_pause_ticks_start();
227 case SDL_KEYUP: /* A keyrelease! */
228 key = event.key.keysym.sym;
230 if(tux.key_event(key, UP))
241 st_pause_ticks_stop();
246 st_pause_ticks_start();
253 tux.size = !tux.size;
256 tux.base.height = 64;
259 tux.base.height = 32;
276 timer_start(&tux.invincible_timer,TUX_INVINCIBLE_TIME);
296 case SDL_JOYAXISMOTION:
297 switch(event.jaxis.axis)
300 if (event.jaxis.value < -JOYSTICK_DEAD_ZONE)
302 tux.input.left = DOWN;
303 tux.input.right = UP;
305 else if (event.jaxis.value > JOYSTICK_DEAD_ZONE)
308 tux.input.right = DOWN;
312 tux.input.left = DOWN;
313 tux.input.right = DOWN;
317 if (event.jaxis.value > JOYSTICK_DEAD_ZONE)
318 tux.input.down = DOWN;
319 else if (event.jaxis.value < -JOYSTICK_DEAD_ZONE)
329 case SDL_JOYBUTTONDOWN:
330 if (event.jbutton.button == JOY_A)
332 else if (event.jbutton.button == JOY_B)
333 tux.input.fire = DOWN;
335 case SDL_JOYBUTTONUP:
336 if (event.jbutton.button == JOY_A)
338 else if (event.jbutton.button == JOY_B)
352 GameSession::action()
354 if (tux.is_dead() || next_level)
356 /* Tux either died, or reached the end of a level! */
361 /* End of a level! */
364 if(st_gl_mode != ST_GL_TEST)
371 world->get_level()->cleanup();
374 world->arrays_free();
383 /* No more lives!? */
387 if(st_gl_mode != ST_GL_TEST)
390 if(st_gl_mode != ST_GL_TEST)
392 if (score > hs_score)
396 world->get_level()->cleanup();
399 world->arrays_free();
401 } /* if (lives < 0) */
404 /* Either way, (re-)load the (next) level... */
408 world->get_level()->cleanup();
410 if (st_gl_mode == ST_GL_LOAD_LEVEL_FILE)
412 if(world->get_level()->load(level_subset) != 0)
417 if(world->get_level()->load(level_subset,level) != 0)
421 world->arrays_free();
422 activate_bad_guys(world->get_level());
423 world->activate_particle_systems();
425 world->get_level()->load_gfx();
427 world->get_level()->load_song();
428 if(st_gl_mode != ST_GL_TEST)
432 play_current_music();
439 /* update particle systems */
440 std::vector<ParticleSystem*>::iterator p;
441 for(p = world->particle_systems.begin(); p != world->particle_systems.end(); ++p)
443 (*p)->simulate(frame_ratio);
446 /* Handle all possible collisions. */
463 int x = screen->h / 20;
464 for(int i = 0; i < x; ++i)
466 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);
468 fillrect(0,0,screen->w,screen->h,rand() % 50, rand() % 50, rand() % 50, 128);
469 text_drawf(&blue_text, "PAUSE - Press 'P' To Play", 0, 230, A_HMIDDLE, A_TOP, 1);
474 menu_process_current();
475 mouse_cursor->draw();
478 /* (Update it all!) */
492 /* --- MAIN GAME LOOP!!! --- */
496 global_frame_counter = 0;
498 timer_init(&fps_timer,true);
499 timer_init(&frame_timer,true);
500 last_update_time = st_get_ticks();
504 clearscreen(0, 0, 0);
508 play_current_music();
510 while (SDL_PollEvent(&event))
518 /* Calculate the movement-factor */
519 frame_ratio = ((double)(update_time-last_update_time))/((double)FRAME_RATE);
520 if(frame_ratio > 1.5) /* Quick hack to correct the unprecise CPU clocks a little bit. */
521 frame_ratio = 1.5 + (frame_ratio - 1.5) * 0.85;
523 if(!timer_check(&frame_timer))
525 timer_start(&frame_timer,25);
526 ++global_frame_counter;
531 tux.input.old_fire = tux.input.fire;
537 if(current_menu == game_menu)
539 switch (game_menu->check())
542 st_pause_ticks_stop();
545 update_load_save_game_menu(save_game_menu, false);
548 update_load_save_game_menu(load_game_menu, true);
551 st_pause_ticks_stop();
556 else if(current_menu == options_menu)
558 process_options_menu();
560 else if(current_menu == save_game_menu )
562 process_save_game_menu();
564 else if(current_menu == load_game_menu )
566 process_load_game_menu();
571 /* Handle actions: */
573 if(!game_pause && !show_menu)
575 /*float z = frame_ratio;
581 /* == 0: no more lives */
582 /* == -1: continues */
594 if(debug_mode && debug_fps)
597 /*Draw the current scene to the screen */
598 /*If the machine running the game is too slow
599 skip the drawing of the frame (so the calculations are more precise and
600 the FPS aren't affected).*/
601 /*if( ! fps_fps < 50.0 )
604 jump = true;*/ /*FIXME: Implement this tweak right.*/
607 /* Time stops in pause mode */
608 if(game_pause || show_menu )
613 /* Set the time of the last update and the time of the current update */
614 last_update_time = update_time;
615 update_time = st_get_ticks();
617 /* Pause till next frame, if the machine running the game is too fast: */
618 /* FIXME: Works great for in OpenGl mode, where the CPU doesn't have to do that much. But
619 the results in SDL mode aren't perfect (thought the 100 FPS are reached), even on an AMD2500+. */
620 if(last_update_time >= update_time - 12 && !jump) {
622 update_time = st_get_ticks();
624 /*if((update_time - last_update_time) < 10)
625 SDL_Delay((11 - (update_time - last_update_time))/2);*/
631 if (timer_check(&time_left))
633 /* are we low on time ? */
634 if ((timer_get_left(&time_left) < TIME_WARNING)
635 && (get_current_music() != HURRYUP_MUSIC)) /* play the fast music */
637 set_current_music(HURRYUP_MUSIC);
638 play_current_music();
646 /* Calculate frames per second */
650 fps_fps = (1000.0 / (float)timer_get_gone(&fps_timer)) * (float)fps_cnt;
652 if(!timer_check(&fps_timer))
654 timer_start(&fps_timer,1000);
660 while (!done && !quit);
665 world->get_level()->cleanup();
668 world->arrays_free();
670 game_started = false;
675 /* Draw a tile on the screen: */
677 void drawshape(float x, float y, unsigned int c, Uint8 alpha)
681 Tile* ptile = TileManager::instance()->get(c);
684 if(ptile->images.size() > 1)
686 texture_draw(&ptile->images[( ((global_frame_counter*25) / ptile->anim_speed) % (ptile->images.size()))],x,y, alpha);
688 else if (ptile->images.size() == 1)
690 texture_draw(&ptile->images[0],x,y, alpha);
694 //printf("Tile not dravable %u\n", c);
700 /* Bounce a brick: */
701 void bumpbrick(float x, float y)
703 world.add_bouncy_brick(((int)(x + 1) / 32) * 32,
706 play_sound(sounds[SND_BRICK], SOUND_CENTER_SPEAKER);
710 void drawstatus(void)
714 sprintf(str, "%d", score);
715 text_draw(&white_text, "SCORE", 0, 0, 1);
716 text_draw(&gold_text, str, 96, 0, 1);
718 if(st_gl_mode != ST_GL_TEST)
720 sprintf(str, "%d", hs_score);
721 text_draw(&white_text, "HIGH", 0, 20, 1);
722 text_draw(&gold_text, str, 96, 20, 1);
726 text_draw(&white_text,"Press ESC To Return",0,20,1);
729 if (timer_get_left(&time_left) > TIME_WARNING || (global_frame_counter % 10) < 5)
731 sprintf(str, "%d", timer_get_left(&time_left) / 1000 );
732 text_draw(&white_text, "TIME", 224, 0, 1);
733 text_draw(&gold_text, str, 304, 0, 1);
736 sprintf(str, "%d", distros);
737 text_draw(&white_text, "DISTROS", screen->h, 0, 1);
738 text_draw(&gold_text, str, 608, 0, 1);
740 text_draw(&white_text, "LIVES", screen->h, 20, 1);
744 sprintf(str, "%2.1f", fps_fps);
745 text_draw(&white_text, "FPS", screen->h, 40, 1);
746 text_draw(&gold_text, str, screen->h + 60, 40, 1);
749 for(int i=0; i < tux.lives; ++i)
751 texture_draw(&tux_life,565+(18*i),20);
756 void drawendscreen(void)
760 clearscreen(0, 0, 0);
762 text_drawf(&blue_text, "GAMEOVER", 0, 200, A_HMIDDLE, A_TOP, 1);
764 sprintf(str, "SCORE: %d", score);
765 text_drawf(&gold_text, str, 0, 224, A_HMIDDLE, A_TOP, 1);
767 sprintf(str, "DISTROS: %d", distros);
768 text_drawf(&gold_text, str, 0, 256, A_HMIDDLE, A_TOP, 1);
773 wait_for_event(event,2000,5000,true);
776 void drawresultscreen(void)
780 clearscreen(0, 0, 0);
782 text_drawf(&blue_text, "Result:", 0, 200, A_HMIDDLE, A_TOP, 1);
784 sprintf(str, "SCORE: %d", score);
785 text_drawf(&gold_text, str, 0, 224, A_HMIDDLE, A_TOP, 1);
787 sprintf(str, "DISTROS: %d", distros);
788 text_drawf(&gold_text, str, 0, 256, A_HMIDDLE, A_TOP, 1);
793 wait_for_event(event,2000,5000,true);
797 GameSession::savegame(int slot)
803 sprintf(savefile,"%s/slot%d.save",st_save_dir,slot);
805 fi = fopen(savefile, "wb");
809 fprintf(stderr, "Warning: I could not open the slot file ");
813 fputs(level_subset, fi);
815 fwrite(&level,sizeof(int),1,fi);
816 fwrite(&score,sizeof(int),1,fi);
817 fwrite(&distros,sizeof(int),1,fi);
818 fwrite(&scroll_x,sizeof(float),1,fi);
819 fwrite(&tux,sizeof(Player),1,fi);
820 timer_fwrite(&tux.invincible_timer,fi);
821 timer_fwrite(&tux.skidding_timer,fi);
822 timer_fwrite(&tux.safe_timer,fi);
823 timer_fwrite(&tux.frame_timer,fi);
824 timer_fwrite(&time_left,fi);
826 fwrite(&ui,sizeof(int),1,fi);
833 GameSession::loadgame(int slot)
840 sprintf(savefile,"%s/slot%d.save",st_save_dir,slot);
842 fi = fopen(savefile, "rb");
846 fprintf(stderr, "Warning: I could not open the slot file ");
852 strcpy(level_subset, str);
853 level_subset[strlen(level_subset)-1] = '\0';
854 fread(&level,sizeof(int),1,fi);
857 world->get_level()->cleanup();
858 if(world->get_level()->load(level_subset,level) != 0)
860 world->arrays_free();
861 activate_bad_guys(world->get_level());
862 world->activate_particle_systems();
864 world->get_level()->load_gfx();
866 world->get_level()->load_song();
868 update_time = st_get_ticks();
870 fread(&score, sizeof(int),1,fi);
871 fread(&distros, sizeof(int),1,fi);
872 fread(&scroll_x,sizeof(float),1,fi);
873 fread(&tux, sizeof(Player), 1, fi);
874 timer_fread(&tux.invincible_timer,fi);
875 timer_fread(&tux.skidding_timer,fi);
876 timer_fread(&tux.safe_timer,fi);
877 timer_fread(&tux.frame_timer,fi);
878 timer_fread(&time_left,fi);
879 fread(&ui,sizeof(int),1,fi);
885 std::string slotinfo(int slot)
892 sprintf(slotfile,"%s/slot%d.save",st_save_dir,slot);
894 fi = fopen(slotfile, "rb");
896 sprintf(tmp,"Slot %d - ",slot);
905 str[strlen(str)-1] = '\0';
907 strcat(tmp, " / Level:");
908 fread(&slot_level,sizeof(int),1,fi);
909 sprintf(str,"%d",slot_level);