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)
370 world->get_level()->free_gfx();
371 world->get_level()->cleanup();
372 world->get_level()->free_song();
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)
395 world->get_level()->free_gfx();
396 world->get_level()->cleanup();
397 world->get_level()->free_song();
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()->free_gfx();
426 world->get_level()->load_gfx();
427 world->get_level()->free_song();
428 world->get_level()->load_song();
430 if(st_gl_mode != ST_GL_TEST)
434 play_current_music();
441 /* update particle systems */
442 std::vector<ParticleSystem*>::iterator p;
443 for(p = world->particle_systems.begin(); p != world->particle_systems.end(); ++p)
445 (*p)->simulate(frame_ratio);
448 /* Handle all possible collisions. */
462 int x = screen->h / 20;
463 for(int i = 0; i < x; ++i)
465 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);
467 fillrect(0,0,screen->w,screen->h,rand() % 50, rand() % 50, rand() % 50, 128);
468 text_drawf(&blue_text, "PAUSE - Press 'P' To Play", 0, 230, A_HMIDDLE, A_TOP, 1);
473 menu_process_current();
474 mouse_cursor->draw();
489 global_frame_counter = 0;
491 timer_init(&fps_timer,true);
492 timer_init(&frame_timer,true);
493 last_update_time = st_get_ticks();
497 clearscreen(0, 0, 0);
501 play_current_music();
503 while (SDL_PollEvent(&event))
510 while (!done && !quit)
512 /* Calculate the movement-factor */
513 frame_ratio = ((double)(update_time-last_update_time))/((double)FRAME_RATE);
514 if(frame_ratio > 1.5) /* Quick hack to correct the unprecise CPU clocks a little bit. */
515 frame_ratio = 1.5 + (frame_ratio - 1.5) * 0.85;
517 if(!timer_check(&frame_timer))
519 timer_start(&frame_timer,25);
520 ++global_frame_counter;
525 tux.input.old_fire = tux.input.fire;
531 if(current_menu == game_menu)
533 switch (game_menu->check())
536 st_pause_ticks_stop();
539 update_load_save_game_menu(save_game_menu, false);
542 update_load_save_game_menu(load_game_menu, true);
545 st_pause_ticks_stop();
550 else if(current_menu == options_menu)
552 process_options_menu();
554 else if(current_menu == save_game_menu )
556 process_save_game_menu();
558 else if(current_menu == load_game_menu )
560 process_load_game_menu();
565 /* Handle actions: */
567 if(!game_pause && !show_menu)
569 /*float z = frame_ratio;
575 /* == 0: no more lives */
576 /* == -1: continues */
588 if(debug_mode && debug_fps)
591 /*Draw the current scene to the screen */
592 /*If the machine running the game is too slow
593 skip the drawing of the frame (so the calculations are more precise and
594 the FPS aren't affected).*/
595 /*if( ! fps_fps < 50.0 )
598 jump = true;*/ /*FIXME: Implement this tweak right.*/
601 /* Time stops in pause mode */
602 if(game_pause || show_menu )
607 /* Set the time of the last update and the time of the current update */
608 last_update_time = update_time;
609 update_time = st_get_ticks();
611 /* Pause till next frame, if the machine running the game is too fast: */
612 /* FIXME: Works great for in OpenGl mode, where the CPU doesn't have to do that much. But
613 the results in SDL mode aren't perfect (thought the 100 FPS are reached), even on an AMD2500+. */
614 if(last_update_time >= update_time - 12) {
616 update_time = st_get_ticks();
618 /*if((update_time - last_update_time) < 10)
619 SDL_Delay((11 - (update_time - last_update_time))/2);*/
622 if (timer_check(&time_left))
624 /* are we low on time ? */
625 if ((timer_get_left(&time_left) < TIME_WARNING)
626 && (get_current_music() != HURRYUP_MUSIC)) /* play the fast music */
628 set_current_music(HURRYUP_MUSIC);
629 play_current_music();
636 /* Calculate frames per second */
640 fps_fps = (1000.0 / (float)timer_get_gone(&fps_timer)) * (float)fps_cnt;
642 if(!timer_check(&fps_timer))
644 timer_start(&fps_timer,1000);
652 world->get_level()->free_gfx();
653 world->get_level()->cleanup();
654 world->get_level()->free_song();
657 world->arrays_free();
659 game_started = false;
664 /* Draw a tile on the screen: */
666 void drawshape(float x, float y, unsigned int c, Uint8 alpha)
670 Tile* ptile = TileManager::instance()->get(c);
673 if(ptile->images.size() > 1)
675 texture_draw(&ptile->images[( ((global_frame_counter*25) / ptile->anim_speed) % (ptile->images.size()))],x,y, alpha);
677 else if (ptile->images.size() == 1)
679 texture_draw(&ptile->images[0],x,y, alpha);
683 //printf("Tile not dravable %u\n", c);
689 /* Bounce a brick: */
690 void bumpbrick(float x, float y)
692 world.add_bouncy_brick(((int)(x + 1) / 32) * 32,
695 play_sound(sounds[SND_BRICK], SOUND_CENTER_SPEAKER);
699 void drawstatus(void)
703 sprintf(str, "%d", score);
704 text_draw(&white_text, "SCORE", 0, 0, 1);
705 text_draw(&gold_text, str, 96, 0, 1);
707 if(st_gl_mode != ST_GL_TEST)
709 sprintf(str, "%d", hs_score);
710 text_draw(&white_text, "HIGH", 0, 20, 1);
711 text_draw(&gold_text, str, 96, 20, 1);
715 text_draw(&white_text,"Press ESC To Return",0,20,1);
718 if (timer_get_left(&time_left) > TIME_WARNING || (global_frame_counter % 10) < 5)
720 sprintf(str, "%d", timer_get_left(&time_left) / 1000 );
721 text_draw(&white_text, "TIME", 224, 0, 1);
722 text_draw(&gold_text, str, 304, 0, 1);
725 sprintf(str, "%d", distros);
726 text_draw(&white_text, "DISTROS", screen->h, 0, 1);
727 text_draw(&gold_text, str, 608, 0, 1);
729 text_draw(&white_text, "LIVES", screen->h, 20, 1);
733 sprintf(str, "%2.1f", fps_fps);
734 text_draw(&white_text, "FPS", screen->h, 40, 1);
735 text_draw(&gold_text, str, screen->h + 60, 40, 1);
738 for(int i=0; i < tux.lives; ++i)
740 texture_draw(&tux_life,565+(18*i),20);
745 void drawendscreen(void)
749 clearscreen(0, 0, 0);
751 text_drawf(&blue_text, "GAMEOVER", 0, 200, A_HMIDDLE, A_TOP, 1);
753 sprintf(str, "SCORE: %d", score);
754 text_drawf(&gold_text, str, 0, 224, A_HMIDDLE, A_TOP, 1);
756 sprintf(str, "DISTROS: %d", distros);
757 text_drawf(&gold_text, str, 0, 256, A_HMIDDLE, A_TOP, 1);
762 wait_for_event(event,2000,5000,true);
765 void drawresultscreen(void)
769 clearscreen(0, 0, 0);
771 text_drawf(&blue_text, "Result:", 0, 200, A_HMIDDLE, A_TOP, 1);
773 sprintf(str, "SCORE: %d", score);
774 text_drawf(&gold_text, str, 0, 224, A_HMIDDLE, A_TOP, 1);
776 sprintf(str, "DISTROS: %d", distros);
777 text_drawf(&gold_text, str, 0, 256, A_HMIDDLE, A_TOP, 1);
782 wait_for_event(event,2000,5000,true);
786 GameSession::savegame(int slot)
792 sprintf(savefile,"%s/slot%d.save",st_save_dir,slot);
794 fi = fopen(savefile, "wb");
798 fprintf(stderr, "Warning: I could not open the slot file ");
802 fputs(level_subset, fi);
804 fwrite(&level,sizeof(int),1,fi);
805 fwrite(&score,sizeof(int),1,fi);
806 fwrite(&distros,sizeof(int),1,fi);
807 fwrite(&scroll_x,sizeof(float),1,fi);
808 fwrite(&tux,sizeof(Player),1,fi);
809 timer_fwrite(&tux.invincible_timer,fi);
810 timer_fwrite(&tux.skidding_timer,fi);
811 timer_fwrite(&tux.safe_timer,fi);
812 timer_fwrite(&tux.frame_timer,fi);
813 timer_fwrite(&time_left,fi);
815 fwrite(&ui,sizeof(int),1,fi);
822 GameSession::loadgame(int slot)
829 sprintf(savefile,"%s/slot%d.save",st_save_dir,slot);
831 fi = fopen(savefile, "rb");
835 fprintf(stderr, "Warning: I could not open the slot file ");
841 strcpy(level_subset, str);
842 level_subset[strlen(level_subset)-1] = '\0';
843 fread(&level,sizeof(int),1,fi);
846 world->get_level()->cleanup();
847 if(world->get_level()->load(level_subset,level) != 0)
849 world->arrays_free();
850 activate_bad_guys(world->get_level());
851 world->activate_particle_systems();
853 world->get_level()->free_gfx();
854 world->get_level()->load_gfx();
855 world->get_level()->free_song();
856 world->get_level()->load_song();
859 update_time = st_get_ticks();
861 fread(&score, sizeof(int),1,fi);
862 fread(&distros, sizeof(int),1,fi);
863 fread(&scroll_x,sizeof(float),1,fi);
864 fread(&tux, sizeof(Player), 1, fi);
865 timer_fread(&tux.invincible_timer,fi);
866 timer_fread(&tux.skidding_timer,fi);
867 timer_fread(&tux.safe_timer,fi);
868 timer_fread(&tux.frame_timer,fi);
869 timer_fread(&time_left,fi);
870 fread(&ui,sizeof(int),1,fi);
876 std::string slotinfo(int slot)
883 sprintf(slotfile,"%s/slot%d.save",st_save_dir,slot);
885 fi = fopen(slotfile, "rb");
887 sprintf(tmp,"Slot %d - ",slot);
896 str[strlen(str)-1] = '\0';
898 strcat(tmp, " / Level:");
899 fread(&slot_level,sizeof(int),1,fi);
900 sprintf(str,"%d",slot_level);