X-Git-Url: https://git.verplant.org/?a=blobdiff_plain;f=src%2Fgameloop.cpp;h=275c42fa62190be20265986908a69430a560d051;hb=32c639e36abc3b7bd99380c8b5da768a127fafd6;hp=5addbe767d88b7363fe5537877ebcf631ea2441d;hpb=49e39f09c2039c91c117e002e363ba33b3cb7e91;p=supertux.git diff --git a/src/gameloop.cpp b/src/gameloop.cpp index 5addbe767..275c42fa6 100644 --- a/src/gameloop.cpp +++ b/src/gameloop.cpp @@ -1,1871 +1,990 @@ -/* - gameloop.c - - Super Tux - Game Loop! - - by Bill Kendrick & Tobias Glaesser - bill@newbreedsoftware.com - http://www.newbreedsoftware.com/supertux/ - - April 11, 2000 - March 15, 2004 -*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifndef WIN32 -#include -#include -#endif - -#include "defines.h" -#include "globals.h" -#include "gameloop.h" -#include "screen.h" -#include "setup.h" -#include "high_scores.h" -#include "menu.h" -#include "badguy.h" -#include "world.h" -#include "special.h" -#include "player.h" -#include "level.h" -#include "scene.h" -#include "collision.h" -#include "tile.h" -#include "particlesystem.h" - -/* extern variables */ - -st_level current_level; -int game_started = false; - -/* Local variables: */ - -static texture_type img_waves[3], img_water, img_pole, img_poletop, img_flag[2]; -static texture_type img_cloud[2][4]; -static SDL_Event event; -static SDLKey key; -static char level_subset[100]; -static float fps_fps; -static int st_gl_mode; -static unsigned int last_update_time; -static unsigned int update_time; -static int pause_menu_frame; -static int debug_fps; - -/* Local function prototypes: */ - -void levelintro(void); -void loadshared(void); -void unloadshared(void); -void drawstatus(void); -void drawendscreen(void); -void drawresultscreen(void); - -void levelintro(void) -{ - char str[60]; - /* Level Intro: */ - clearscreen(0, 0, 0); - - sprintf(str, "LEVEL %d", level); - text_drawf(&blue_text, str, 0, 200, A_HMIDDLE, A_TOP, 1); - - sprintf(str, "%s", current_level.name.c_str()); - text_drawf(&gold_text, str, 0, 224, A_HMIDDLE, A_TOP, 1); - - sprintf(str, "TUX x %d", tux.lives); - text_drawf(&white_text, str, 0, 256, A_HMIDDLE, A_TOP, 1); - - flipscreen(); - - SDL_Delay(1000); -} - -/* Reset Timers */ -void start_timers(void) -{ - timer_start(&time_left,current_level.time_left*1000); - st_pause_ticks_init(); - update_time = st_get_ticks(); -} - -void activate_bad_guys(void) -{ - int x,y; - - /* Activate bad guys: */ - - for (y = 0; y < 15; y++) - { - for (x = 0; x < current_level.width; x++) - { - if (current_level.dn_tiles[y][x] >= '0' && current_level.dn_tiles[y][x] <= '9') - { - add_bad_guy(x * 32, y * 32, static_cast(current_level.dn_tiles[y][x] - '0')); - current_level.dn_tiles[y][x] = 0; - } - } - } -} - -void activate_particle_systems(void) -{ - printf("PSys: %s\n", current_level.particle_system.c_str()); - if(current_level.particle_system == "clouds") - { - particle_systems.push_back(new CloudParticleSystem); - } - else if(current_level.particle_system == "snow") - { - particle_systems.push_back(new SnowParticleSystem); - } - else if(current_level.particle_system != "") - { - st_abort("unknown particle system specified in level", ""); - } -} - -/* --- GAME EVENT! --- */ - -void game_event(void) -{ - while (SDL_PollEvent(&event)) - { - switch(event.type) - { - case SDL_QUIT: /* Quit event - quit: */ - quit = 1; - break; - case SDL_KEYDOWN: /* A keypress! */ - key = event.key.keysym.sym; - - /* Check for menu-events, if the menu is shown */ - if(show_menu) - menu_event(&event.key.keysym); - - if(tux.key_event(key,DOWN)) - break; - - switch(key) - { - case SDLK_ESCAPE: /* Escape: Open/Close the menu: */ - if(!game_pause) - { - if(st_gl_mode == ST_GL_TEST) - quit = 1; - else if(show_menu) - { - Menu::set_current(game_menu); - show_menu = 0; - st_pause_ticks_stop(); - } - else - { - Menu::set_current(game_menu); - show_menu = 1; - st_pause_ticks_start(); - } - } - break; - default: - break; - } - break; - case SDL_KEYUP: /* A keyrelease! */ - key = event.key.keysym.sym; - - if(tux.key_event(key, UP)) - break; - - switch(key) - { - case SDLK_p: - if(!show_menu) - { - if(game_pause) - { - game_pause = 0; - st_pause_ticks_stop(); - } - else - { - game_pause = 1; - st_pause_ticks_start(); - } - } - break; - case SDLK_TAB: - if(debug_mode) - { - tux.size = !tux.size; - if(tux.size == BIG) - { - tux.base.height = 64; - } - else - tux.base.height = 32; - } - break; - case SDLK_END: - if(debug_mode) - distros += 50; - break; - case SDLK_SPACE: - if(debug_mode) - next_level = 1; - break; - case SDLK_DELETE: - if(debug_mode) - tux.got_coffee = 1; - break; - case SDLK_INSERT: - if(debug_mode) - timer_start(&tux.invincible_timer,TUX_INVINCIBLE_TIME); - break; - case SDLK_l: - if(debug_mode) - --tux.lives; - break; - case SDLK_s: - if(debug_mode) - score += 1000; - case SDLK_f: - if(debug_fps) - debug_fps = false; - else - debug_fps = true; - break; - default: - break; - } - break; - - case SDL_JOYAXISMOTION: - switch(event.jaxis.axis) - { - case JOY_X: - if (event.jaxis.value < -JOYSTICK_DEAD_ZONE) - { - tux.input.left = DOWN; - tux.input.right = UP; - } - else if (event.jaxis.value > JOYSTICK_DEAD_ZONE) - { - tux.input.left = UP; - tux.input.right = DOWN; - } - else - { - tux.input.left = DOWN; - tux.input.right = DOWN; - } - break; - case JOY_Y: - if (event.jaxis.value > JOYSTICK_DEAD_ZONE) - tux.input.down = DOWN; - else if (event.jaxis.value < -JOYSTICK_DEAD_ZONE) - tux.input.down = UP; - else - tux.input.down = UP; - - /* Handle joystick for the menu */ - if(show_menu) - { - if(tux.input.down == DOWN) - menuaction = MENU_ACTION_DOWN; - else - menuaction = MENU_ACTION_UP; - } - break; - default: - break; - } - break; - case SDL_JOYBUTTONDOWN: - if (event.jbutton.button == JOY_A) - tux.input.up = DOWN; - else if (event.jbutton.button == JOY_B) - tux.input.fire = DOWN; - break; - case SDL_JOYBUTTONUP: - if (event.jbutton.button == JOY_A) - tux.input.up = UP; - else if (event.jbutton.button == JOY_B) - tux.input.fire = UP; - - if(show_menu) - menuaction = MENU_ACTION_HIT; - break; - - default: - break; - - } /* switch */ - - } /* while */ -} - -/* --- GAME ACTION! --- */ - -int game_action(void) -{ - unsigned int i; - - /* (tux_dying || next_level) */ - if (tux.dying || next_level) - { - /* Tux either died, or reached the end of a level! */ - - halt_music(); - - - if (next_level) - { - /* End of a level! */ - level++; - next_level = 0; - if(st_gl_mode != ST_GL_TEST) - { - drawresultscreen(); - } - else - { - level_free_gfx(); - level_free(¤t_level); - level_free_song(); - unloadshared(); - arrays_free(); - return(0); - } - tux.level_begin(); - } - else - { - tux.is_dying(); - - /* No more lives!? */ - - if (tux.lives < 0) - { - if(st_gl_mode != ST_GL_TEST) - drawendscreen(); - - if(st_gl_mode != ST_GL_TEST) - { - if (score > hs_score) - save_hs(score); - } - level_free_gfx(); - level_free(¤t_level); - level_free_song(); - unloadshared(); - arrays_free(); - return(0); - } /* if (lives < 0) */ - } - - /* Either way, (re-)load the (next) level... */ - - tux.level_begin(); - set_defaults(); - level_free(¤t_level); - - if (st_gl_mode == ST_GL_LOAD_LEVEL_FILE) - { - if(level_load(¤t_level, level_subset) != 0) - return 0; - } - else - { - if(level_load(¤t_level,level_subset,level) != 0) - return 0; - } - - arrays_free(); - arrays_init(); - activate_bad_guys(); - activate_particle_systems(); - level_free_gfx(); - level_load_gfx(¤t_level); - level_free_song(); - level_load_song(¤t_level); - if(st_gl_mode != ST_GL_TEST) - levelintro(); - start_timers(); - /* Play music: */ - play_current_music(); - } - - tux.action(); - - /* Handle bouncy distros: */ - for (i = 0; i < bouncy_distros.size(); i++) - { - bouncy_distro_action(&bouncy_distros[i]); - } - - - /* Handle broken bricks: */ - for (i = 0; i < broken_bricks.size(); i++) - { - broken_brick_action(&broken_bricks[i]); - } - - - /* Handle distro counting: */ - - if (counting_distros) - { - distro_counter--; - - if (distro_counter <= 0) - counting_distros = -1; - } - - - /* Handle bouncy bricks: */ - - for (i = 0; i < bouncy_bricks.size(); i++) - { - bouncy_brick_action(&bouncy_bricks[i]); - } - - - /* Handle floating scores: */ - - for (i = 0; i < floating_scores.size(); i++) - { - floating_score_action(&floating_scores[i]); - } - - - /* Handle bullets: */ - - for (i = 0; i < bullets.size(); ++i) - { - bullet_action(&bullets[i]); - } - - /* Handle upgrades: */ - - for (i = 0; i < upgrades.size(); i++) - { - upgrade_action(&upgrades[i]); - } - - - /* Handle bad guys: */ - - for (i = 0; i < bad_guys.size(); i++) - { - bad_guys[i].action(); - } - - /* update particle systems */ - std::vector::iterator p; - for(p = particle_systems.begin(); p != particle_systems.end(); ++p) - { - (*p)->simulate(frame_ratio); - } - - /* Handle all possible collisions. */ - collision_handler(); - - return -1; -} - -/* --- GAME DRAW! --- */ - -void game_draw(void) -{ -int y,x; - - /* Draw screen: */ - if (tux.dying && (global_frame_counter % 4) == 0) - clearscreen(255, 255, 255); - else if(timer_check(&super_bkgd_timer)) - texture_draw(&img_super_bkgd, 0, 0); - else - { - /* Draw the real background */ - if(current_level.bkgd_image[0] != '\0') - { - int s = (int)scroll_x / 30; - texture_draw_part(&img_bkgd,s,0,0,0,img_bkgd.w - s, img_bkgd.h); - texture_draw_part(&img_bkgd,0,0,screen->w - s ,0,s,img_bkgd.h); - } - else - { - clearscreen(current_level.bkgd_red, current_level.bkgd_green, current_level.bkgd_blue); - } - } - - /* Draw particle systems (background) */ - std::vector::iterator p; - for(p = particle_systems.begin(); p != particle_systems.end(); ++p) - { - (*p)->draw(scroll_x, 0, 0); - } - - /* Draw background: */ - - for (y = 0; y < 15; ++y) - { - for (x = 0; x < 21; ++x) - { - drawshape(32*x - fmodf(scroll_x, 32), y * 32, - current_level.bg_tiles[(int)y][(int)x + (int)(scroll_x / 32)]); - } - } - - /* Draw interactive tiles: */ - - for (y = 0; y < 15; ++y) - { - for (x = 0; x < 21; ++x) - { - drawshape(32*x - fmodf(scroll_x, 32), y * 32, - current_level.ia_tiles[(int)y][(int)x + (int)(scroll_x / 32)]); - } - } - - /* (Bouncy bricks): */ - - for (unsigned int i = 0; i < bouncy_bricks.size(); ++i) - bouncy_brick_draw(&bouncy_bricks[i]); - - for (unsigned int i = 0; i < bad_guys.size(); ++i) - bad_guys[i].draw(); - - tux.draw(); - - for (unsigned int i = 0; i < bullets.size(); ++i) - bullet_draw(&bullets[i]); - - for (unsigned int i = 0; i < floating_scores.size(); ++i) - floating_score_draw(&floating_scores[i]); - - for (unsigned int i = 0; i < upgrades.size(); ++i) - upgrade_draw(&upgrades[i]); - - for (unsigned int i = 0; i < bouncy_distros.size(); ++i) - bouncy_distro_draw(&bouncy_distros[i]); - - for (unsigned int i = 0; i < broken_bricks.size(); ++i) - broken_brick_draw(&broken_bricks[i]); - - /* Draw foreground: */ - - for (y = 0; y < 15; ++y) - { - for (x = 0; x < 21; ++x) - { - drawshape(32*x - fmodf(scroll_x, 32), y * 32, - current_level.fg_tiles[(int)y][(int)x + (int)(scroll_x / 32)]); - } - } - - /* Draw particle systems (foreground) */ - for(p = particle_systems.begin(); p != particle_systems.end(); ++p) - { - (*p)->draw(scroll_x, 0, 1); - } - - drawstatus(); - - if(game_pause) - { - int x = screen->h / 20; - for(int i = 0; i < x; ++i) - { - 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); - } - fillrect(0,0,screen->w,screen->h,rand() % 50, rand() % 50, rand() % 50, 128); - text_drawf(&blue_text, "PAUSE - Press 'P' To Play", 0, 230, A_HMIDDLE, A_TOP, 1); - } - - if(show_menu) - menu_process_current(); - - /* (Update it all!) */ - updatescreen(); -} - -/* --- GAME LOOP! --- */ - -int gameloop(const char * subset, int levelnb, int mode) -{ - int fps_cnt, jump, done; - timer_type fps_timer, frame_timer; - timer_init(&fps_timer, true); - timer_init(&frame_timer, true); - - game_started = true; - - st_gl_mode = mode; - level = levelnb; - - /* Init the game: */ - arrays_init(); - set_defaults(); - - strcpy(level_subset,subset); - - if (st_gl_mode == ST_GL_LOAD_LEVEL_FILE) - { - if (level_load(¤t_level, level_subset)) - exit(1); - } - else - { - if(level_load(¤t_level, level_subset, level) != 0) - exit(1); - } - - level_load_gfx(¤t_level); - activate_bad_guys(); - activate_particle_systems(); - level_load_song(¤t_level); - - tux.init(); - - if(st_gl_mode != ST_GL_TEST) - load_hs(); - - loadshared(); - - if(st_gl_mode == ST_GL_PLAY || st_gl_mode == ST_GL_LOAD_LEVEL_FILE) - levelintro(); - - timer_init(&time_left,true); - start_timers(); - - if(st_gl_mode == ST_GL_LOAD_GAME) - loadgame(levelnb); - - /* --- MAIN GAME LOOP!!! --- */ - - jump = false; - done = 0; - quit = 0; - global_frame_counter = 0; - game_pause = 0; - timer_init(&fps_timer,true); - timer_init(&frame_timer,true); - fps_cnt = 0; - - /* Clear screen: */ - - clearscreen(0, 0, 0); - updatescreen(); - - /* Play music: */ - play_current_music(); - - - while (SDL_PollEvent(&event)) - {} - - game_draw(); - do - { - jump = false; - - /* Calculate the movement-factor */ - frame_ratio = ((double)(update_time-last_update_time))/((double)FRAME_RATE); - if(frame_ratio > 1.5) /* Quick hack to correct the unprecise CPU clocks a little bit. */ - frame_ratio = 1.5 + (frame_ratio - 1.5) * 0.85; - - if(!timer_check(&frame_timer)) - { - timer_start(&frame_timer,25); - ++global_frame_counter; - } - - /* Handle events: */ - - tux.input.old_fire = tux.input.fire; - - game_event(); - - if(show_menu) - { - if(current_menu == game_menu) - { - switch (game_menu->check()) - { - case 2: - st_pause_ticks_stop(); - break; - case 3: - update_load_save_game_menu(save_game_menu, false); - break; - case 4: - update_load_save_game_menu(load_game_menu, true); - break; - case 7: - st_pause_ticks_stop(); - done = 1; - break; - } - } - else if(current_menu == options_menu) - { - process_options_menu(); - } - else if(current_menu == save_game_menu ) - { - process_save_load_game_menu(true); - } - else if(current_menu == load_game_menu ) - { - process_save_load_game_menu(false); - } - } - - - /* Handle actions: */ - - if(!game_pause && !show_menu) - { - /*float z = frame_ratio; - frame_ratio = 1; - while(z >= 1) - {*/ - if (game_action() == 0) - { - /* == 0: no more lives */ - /* == -1: continues */ - return 0; - } - /* --z; - }*/ - } - else - { - ++pause_menu_frame; - SDL_Delay(50); - } - - if(debug_mode && debug_fps) - SDL_Delay(60); - - /*Draw the current scene to the screen */ - /*If the machine running the game is too slow - skip the drawing of the frame (so the calculations are more precise and - the FPS aren't affected).*/ - /*if( ! fps_fps < 50.0 ) - game_draw(); - else - jump = true;*/ /*FIXME: Implement this tweak right.*/ - game_draw(); - - /* Time stops in pause mode */ - if(game_pause || show_menu ) - { - continue; - } - - /* Set the time of the last update and the time of the current update */ - last_update_time = update_time; - update_time = st_get_ticks(); - - /* Pause till next frame, if the machine running the game is too fast: */ - /* FIXME: Works great for in OpenGl mode, where the CPU doesn't have to do that much. But - the results in SDL mode aren't perfect (thought the 100 FPS are reached), even on an AMD2500+. */ - if(last_update_time >= update_time - 12 && !jump) - SDL_Delay(10); - /*if((update_time - last_update_time) < 10) - SDL_Delay((11 - (update_time - last_update_time))/2);*/ - - - - /* Handle time: */ - - if (timer_check(&time_left)) - { - /* are we low on time ? */ - if ((timer_get_left(&time_left) < TIME_WARNING) - && (get_current_music() != HURRYUP_MUSIC)) /* play the fast music */ - { - set_current_music(HURRYUP_MUSIC); - play_current_music(); - } - - } - else - tux.kill(KILL); - - - /* Calculate frames per second */ - if(show_fps) - { - ++fps_cnt; - fps_fps = (1000.0 / (float)timer_get_gone(&fps_timer)) * (float)fps_cnt; - - if(!timer_check(&fps_timer)) - { - timer_start(&fps_timer,1000); - fps_cnt = 0; - } - } - - } - while (!done && !quit); - - halt_music(); - - level_free_gfx(); - level_free(¤t_level); - level_free_song(); - unloadshared(); - arrays_free(); - - game_started = false; - - return(quit); -} - - -/* Load graphics/sounds shared between all levels: */ - -void loadshared(void) -{ - int i; - - /* Tuxes: */ - texture_load(&tux_right[0], datadir + "/images/shared/tux-right-0.png", USE_ALPHA); - texture_load(&tux_right[1], datadir + "/images/shared/tux-right-1.png", USE_ALPHA); - texture_load(&tux_right[2], datadir + "/images/shared/tux-right-2.png", USE_ALPHA); - - texture_load(&tux_left[0], datadir + "/images/shared/tux-left-0.png", USE_ALPHA); - texture_load(&tux_left[1], datadir + "/images/shared/tux-left-1.png", USE_ALPHA); - texture_load(&tux_left[2], datadir + "/images/shared/tux-left-2.png", USE_ALPHA); - - texture_load(&firetux_right[0], datadir + "/images/shared/firetux-right-0.png", USE_ALPHA); - texture_load(&firetux_right[1], datadir + "/images/shared/firetux-right-1.png", USE_ALPHA); - texture_load(&firetux_right[2], datadir + "/images/shared/firetux-right-2.png", USE_ALPHA); - - texture_load(&firetux_left[0], datadir + "/images/shared/firetux-left-0.png", USE_ALPHA); - texture_load(&firetux_left[1], datadir + "/images/shared/firetux-left-1.png", USE_ALPHA); - texture_load(&firetux_left[2], datadir + "/images/shared/firetux-left-2.png", USE_ALPHA); - - - texture_load(&cape_right[0], datadir + "/images/shared/cape-right-0.png", - USE_ALPHA); - - texture_load(&cape_right[1], datadir + "/images/shared/cape-right-1.png", - USE_ALPHA); - - texture_load(&cape_left[0], datadir + "/images/shared/cape-left-0.png", - USE_ALPHA); - - texture_load(&cape_left[1], datadir + "/images/shared/cape-left-1.png", - USE_ALPHA); - - texture_load(&bigtux_right[0], datadir + "/images/shared/bigtux-right-0.png", - USE_ALPHA); - - texture_load(&bigtux_right[1], datadir + "/images/shared/bigtux-right-1.png", - USE_ALPHA); - - texture_load(&bigtux_right[2], datadir + "/images/shared/bigtux-right-2.png", - USE_ALPHA); - - texture_load(&bigtux_right_jump, datadir + "/images/shared/bigtux-right-jump.png", USE_ALPHA); - - texture_load(&bigtux_left[0], datadir + "/images/shared/bigtux-left-0.png", - USE_ALPHA); - - texture_load(&bigtux_left[1], datadir + "/images/shared/bigtux-left-1.png", - USE_ALPHA); - - texture_load(&bigtux_left[2], datadir + "/images/shared/bigtux-left-2.png", - USE_ALPHA); - - texture_load(&bigtux_left_jump, datadir + "/images/shared/bigtux-left-jump.png", USE_ALPHA); - - texture_load(&bigcape_right[0], datadir + "/images/shared/bigcape-right-0.png", - USE_ALPHA); - - texture_load(&bigcape_right[1], datadir + "/images/shared/bigcape-right-1.png", - USE_ALPHA); - - texture_load(&bigcape_left[0], datadir + "/images/shared/bigcape-left-0.png", - USE_ALPHA); - - texture_load(&bigcape_left[1], datadir + "/images/shared/bigcape-left-1.png", - USE_ALPHA); - - texture_load(&bigfiretux_right[0], datadir + "/images/shared/bigfiretux-right-0.png", - USE_ALPHA); - - texture_load(&bigfiretux_right[1], datadir + "/images/shared/bigfiretux-right-1.png", - USE_ALPHA); - - texture_load(&bigfiretux_right[2], datadir + "/images/shared/bigfiretux-right-2.png", - USE_ALPHA); - - texture_load(&bigfiretux_right_jump, datadir + "/images/shared/bigfiretux-right-jump.png", USE_ALPHA); - - texture_load(&bigfiretux_left[0], datadir + "/images/shared/bigfiretux-left-0.png", - USE_ALPHA); - - texture_load(&bigfiretux_left[1], datadir + "/images/shared/bigfiretux-left-1.png", - USE_ALPHA); - - texture_load(&bigfiretux_left[2], datadir + "/images/shared/bigfiretux-left-2.png", - USE_ALPHA); - - texture_load(&bigfiretux_left_jump, datadir + "/images/shared/bigfiretux-left-jump.png", USE_ALPHA); - - texture_load(&bigcape_right[0], datadir + "/images/shared/bigcape-right-0.png", - USE_ALPHA); - - texture_load(&bigcape_right[1], datadir + "/images/shared/bigcape-right-1.png", - USE_ALPHA); - - texture_load(&bigcape_left[0], datadir + "/images/shared/bigcape-left-0.png", - USE_ALPHA); - - texture_load(&bigcape_left[1], datadir + "/images/shared/bigcape-left-1.png", - USE_ALPHA); - - - texture_load(&ducktux_right, datadir + - "/images/shared/ducktux-right.png", - USE_ALPHA); - - texture_load(&ducktux_left, datadir + - "/images/shared/ducktux-left.png", - USE_ALPHA); - - texture_load(&skidtux_right, datadir + - "/images/shared/skidtux-right.png", - USE_ALPHA); - - texture_load(&skidtux_left, datadir + - "/images/shared/skidtux-left.png", - USE_ALPHA); - - texture_load(&duckfiretux_right, datadir + - "/images/shared/duckfiretux-right.png", - USE_ALPHA); - - texture_load(&duckfiretux_left, datadir + - "/images/shared/duckfiretux-left.png", - USE_ALPHA); - - texture_load(&skidfiretux_right, datadir + - "/images/shared/skidfiretux-right.png", - USE_ALPHA); - - texture_load(&skidfiretux_left, datadir + - "/images/shared/skidfiretux-left.png", - USE_ALPHA); - - - /* Boxes: */ - - texture_load(&img_box_full, datadir + "/images/shared/box-full.png", - IGNORE_ALPHA); - texture_load(&img_box_empty, datadir + "/images/shared/box-empty.png", - IGNORE_ALPHA); - - - /* Water: */ - - - texture_load(&img_water, datadir + "/images/shared/water.png", IGNORE_ALPHA); - - texture_load(&img_waves[0], datadir + "/images/shared/waves-0.png", - USE_ALPHA); - - texture_load(&img_waves[1], datadir + "/images/shared/waves-1.png", - USE_ALPHA); - - texture_load(&img_waves[2], datadir + "/images/shared/waves-2.png", - USE_ALPHA); - - - /* Pole: */ - - texture_load(&img_pole, datadir + "/images/shared/pole.png", USE_ALPHA); - texture_load(&img_poletop, datadir + "/images/shared/poletop.png", - USE_ALPHA); - - - /* Flag: */ - - texture_load(&img_flag[0], datadir + "/images/shared/flag-0.png", - USE_ALPHA); - texture_load(&img_flag[1], datadir + "/images/shared/flag-1.png", - USE_ALPHA); - - - /* Cloud: */ - - texture_load(&img_cloud[0][0], datadir + "/images/shared/cloud-00.png", - USE_ALPHA); - - texture_load(&img_cloud[0][1], datadir + "/images/shared/cloud-01.png", - USE_ALPHA); - - texture_load(&img_cloud[0][2], datadir + "/images/shared/cloud-02.png", - USE_ALPHA); - - texture_load(&img_cloud[0][3], datadir + "/images/shared/cloud-03.png", - USE_ALPHA); - - - texture_load(&img_cloud[1][0], datadir + "/images/shared/cloud-10.png", - USE_ALPHA); - - texture_load(&img_cloud[1][1], datadir + "/images/shared/cloud-11.png", - USE_ALPHA); - - texture_load(&img_cloud[1][2], datadir + "/images/shared/cloud-12.png", - USE_ALPHA); - - texture_load(&img_cloud[1][3], datadir + "/images/shared/cloud-13.png", - USE_ALPHA); - - - /* Bad guys: */ - - /* (BSOD) */ - - texture_load(&img_bsod_left[0], datadir + - "/images/shared/bsod-left-0.png", - USE_ALPHA); - - texture_load(&img_bsod_left[1], datadir + - "/images/shared/bsod-left-1.png", - USE_ALPHA); - - texture_load(&img_bsod_left[2], datadir + - "/images/shared/bsod-left-2.png", - USE_ALPHA); - - texture_load(&img_bsod_left[3], datadir + - "/images/shared/bsod-left-3.png", - USE_ALPHA); - - texture_load(&img_bsod_right[0], datadir + - "/images/shared/bsod-right-0.png", - USE_ALPHA); - - texture_load(&img_bsod_right[1], datadir + - "/images/shared/bsod-right-1.png", - USE_ALPHA); - - texture_load(&img_bsod_right[2], datadir + - "/images/shared/bsod-right-2.png", - USE_ALPHA); - - texture_load(&img_bsod_right[3], datadir + - "/images/shared/bsod-right-3.png", - USE_ALPHA); - - texture_load(&img_bsod_squished_left, datadir + - "/images/shared/bsod-squished-left.png", - USE_ALPHA); - - texture_load(&img_bsod_squished_right, datadir + - "/images/shared/bsod-squished-right.png", - USE_ALPHA); - - texture_load(&img_bsod_falling_left, datadir + - "/images/shared/bsod-falling-left.png", - USE_ALPHA); - - texture_load(&img_bsod_falling_right, datadir + - "/images/shared/bsod-falling-right.png", - USE_ALPHA); - - - /* (Laptop) */ - - texture_load(&img_laptop_left[0], datadir + - "/images/shared/laptop-left-0.png", - USE_ALPHA); - - texture_load(&img_laptop_left[1], datadir + - "/images/shared/laptop-left-1.png", - USE_ALPHA); - - texture_load(&img_laptop_left[2], datadir + - "/images/shared/laptop-left-2.png", - USE_ALPHA); - - texture_load(&img_laptop_right[0], datadir + - "/images/shared/laptop-right-0.png", - USE_ALPHA); - - texture_load(&img_laptop_right[1], datadir + - "/images/shared/laptop-right-1.png", - USE_ALPHA); - - texture_load(&img_laptop_right[2], datadir + - "/images/shared/laptop-right-2.png", - USE_ALPHA); - - texture_load(&img_laptop_flat_left, datadir + - "/images/shared/laptop-flat-left.png", - USE_ALPHA); - - texture_load(&img_laptop_flat_right, datadir + - "/images/shared/laptop-flat-right.png", - USE_ALPHA); - - texture_load(&img_laptop_falling_left, datadir + - "/images/shared/laptop-falling-left.png", - USE_ALPHA); - - texture_load(&img_laptop_falling_right, datadir + - "/images/shared/laptop-falling-right.png", - USE_ALPHA); - - - /* (Money) */ - - texture_load(&img_money_left[0], datadir + - "/images/shared/bag-left-0.png", - USE_ALPHA); - - texture_load(&img_money_left[1], datadir + - "/images/shared/bag-left-1.png", - USE_ALPHA); - - texture_load(&img_money_right[0], datadir + - "/images/shared/bag-right-0.png", - USE_ALPHA); - - texture_load(&img_money_right[1], datadir + - "/images/shared/bag-right-1.png", - USE_ALPHA); - - - - /* Upgrades: */ - - texture_load(&img_mints, datadir + "/images/shared/mints.png", USE_ALPHA); - texture_load(&img_coffee, datadir + "/images/shared/coffee.png", USE_ALPHA); - +// $Id$ +// +// SuperTux +// Copyright (C) 2000 Bill Kendrick +// Copyright (C) 2004 Tobias Glaesser +// Copyright (C) 2004 Ingo Ruhnke +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include - /* Weapons: */ +#include "SDL.h" - texture_load(&img_bullet, datadir + "/images/shared/bullet.png", USE_ALPHA); +#ifndef WIN32 +#include +#include +#endif - texture_load(&img_red_glow, datadir + "/images/shared/red-glow.png", - USE_ALPHA); +#include "defines.h" +#include "app/globals.h" +#include "gameloop.h" +#include "video/screen.h" +#include "app/setup.h" +#include "gui/menu.h" +#include "sector.h" +#include "level.h" +#include "scene.h" +#include "tile.h" +#include "object/particlesystem.h" +#include "object/background.h" +#include "object/tilemap.h" +#include "object/camera.h" +#include "object/player.h" +#include "lisp/lisp.h" +#include "lisp/parser.h" +#include "resources.h" +#include "app/gettext.h" +#include "worldmap.h" +#include "intro.h" +#include "misc.h" +#include "statistics.h" +#include "timer.h" +#include "object/fireworks.h" + +GameSession* GameSession::current_ = 0; + +bool compare_last(std::string& haystack, std::string needle) +{ + int haystack_size = haystack.size(); + int needle_size = needle.size(); + if(haystack_size < needle_size) + return false; + if(haystack.compare(haystack_size-needle_size, needle_size, needle) == 0) + return true; + return false; +} - /* Distros: */ +GameSession::GameSession(const std::string& levelfile_, int mode, + Statistics* statistics) + : level(0), currentsector(0), st_gl_mode(mode), + end_sequence(NO_ENDSEQUENCE), levelfile(levelfile_), + best_level_statistics(statistics) +{ + current_ = this; + + game_pause = false; + fps_fps = 0; - texture_load(&img_distro[0], datadir + "/images/shared/distro-0.png", - USE_ALPHA); + context = new DrawingContext(); - texture_load(&img_distro[1], datadir + "/images/shared/distro-1.png", - USE_ALPHA); + last_swap_point = Vector(-1, -1); + last_swap_stats.reset(); - texture_load(&img_distro[2], datadir + "/images/shared/distro-2.png", - USE_ALPHA); + restart_level(); +} - texture_load(&img_distro[3], datadir + "/images/shared/distro-3.png", - USE_ALPHA); +void +GameSession::restart_level() +{ + game_pause = false; + exit_status = ES_NONE; + end_sequence = NO_ENDSEQUENCE; + last_keys.clear(); - /* Tux life: */ +#if 0 + Vector tux_pos = Vector(-1,-1); + if (currentsector) + { + // Tux has lost a life, so we try to respawn him at the nearest reset point + tux_pos = currentsector->player->base; + } +#endif + + delete level; + currentsector = 0; - texture_load(&tux_life, datadir + "/images/shared/tux-life.png", - USE_ALPHA); + level = new Level; + level->load(levelfile); - /* Herring: */ + global_stats.reset(); + global_stats.set_total_points(COINS_COLLECTED_STAT, level->get_total_coins()); + global_stats.set_total_points(BADGUYS_KILLED_STAT, level->get_total_badguys()); + global_stats.set_total_points(TIME_NEEDED_STAT, level->timelimit); - texture_load(&img_golden_herring, datadir + "/images/shared/golden-herring.png", - USE_ALPHA); + currentsector = level->get_sector("main"); + if(!currentsector) + Termination::abort("Level has no main sector.", ""); + currentsector->activate("main"); +#if 0 + // Set Tux to the nearest reset point + if(tux_pos.x != -1) + { + tux_pos = currentsector->get_best_spawn_point(tux_pos); - /* Super background: */ + if(last_swap_point.x > tux_pos.x) + tux_pos = last_swap_point; + else // new swap point + { + last_swap_point = tux_pos; - texture_load(&img_super_bkgd, datadir + "/images/shared/super-bkgd.png", - IGNORE_ALPHA); + last_swap_stats += global_stats; + } + currentsector->player->base.x = tux_pos.x; + currentsector->player->base.y = tux_pos.y; - /* Sound effects: */ + // has to reset camera on swapping + currentsector->camera->reset(Vector(currentsector->player->base.x, + currentsector->player->base.y)); + } +#endif - /* if (use_sound) // this will introduce SERIOUS bugs here ! because "load_sound" - // initialize sounds[i] with the correct pointer's value: - // NULL or something else. And it will be dangerous to - // play with not-initialized pointers. - // This is also true with if (use_music) - Send a mail to me: neoneurone@users.sf.net, if you have another opinion. :) - */ - for (i = 0; i < NUM_SOUNDS; i++) - sounds[i] = load_sound(datadir + soundfilenames[i]); + if (st_gl_mode != ST_GL_DEMO_GAME) + { + if(st_gl_mode == ST_GL_PLAY || st_gl_mode == ST_GL_LOAD_LEVEL_FILE) + levelintro(); + } - /* Herring song */ - herring_song = load_song(datadir + "/music/SALCON.MOD"); + start_timers(); + currentsector->play_music(LEVEL_MUSIC); } +GameSession::~GameSession() +{ + delete level; + delete context; +} -/* Free shared data: */ - -void unloadshared(void) +void +GameSession::levelintro(void) { - int i; + SoundManager::get()->halt_music(); + + char str[60]; - for (i = 0; i < 3; i++) - { - texture_free(&tux_right[i]); - texture_free(&tux_left[i]); - texture_free(&bigtux_right[i]); - texture_free(&bigtux_left[i]); + DrawingContext context; + for(Sector::GameObjects::iterator i = currentsector->gameobjects.begin(); + i != currentsector->gameobjects.end(); ++i) { + Background* background = dynamic_cast (*i); + if(background) { + background->draw(context); } + } - texture_free(&bigtux_right_jump); - texture_free(&bigtux_left_jump); - - for (i = 0; i < 2; i++) - { - texture_free(&cape_right[i]); - texture_free(&cape_left[i]); - texture_free(&bigcape_right[i]); - texture_free(&bigcape_left[i]); - } +// context.draw_text(gold_text, level->get_name(), Vector(screen->w/2, 160), +// CENTER_ALLIGN, LAYER_FOREGROUND1); + context.draw_center_text(gold_text, level->get_name(), Vector(0, 160), + LAYER_FOREGROUND1); - texture_free(&ducktux_left); - texture_free(&ducktux_right); + sprintf(str, "TUX x %d", player_status.lives); + context.draw_text(white_text, str, Vector(screen->w/2, 210), + CENTER_ALLIGN, LAYER_FOREGROUND1); - texture_free(&skidtux_left); - texture_free(&skidtux_right); + if((level->get_author().size()) && (level->get_author() != "SuperTux Team")) + //TODO make author check case/blank-insensitive + context.draw_text(white_small_text, + std::string(_("contributed by ")) + level->get_author(), + Vector(screen->w/2, 350), CENTER_ALLIGN, LAYER_FOREGROUND1); - for (i = 0; i < 4; i++) - { - texture_free(&img_bsod_left[i]); - texture_free(&img_bsod_right[i]); - } - texture_free(&img_bsod_squished_left); - texture_free(&img_bsod_squished_right); + if(best_level_statistics != NULL) + best_level_statistics->draw_message_info(context, _("Best Level Statistics")); - texture_free(&img_bsod_falling_left); - texture_free(&img_bsod_falling_right); + context.do_drawing(); - for (i = 0; i < 3; i++) - { - texture_free(&img_laptop_left[i]); - texture_free(&img_laptop_right[i]); - } + SDL_Event event; + wait_for_event(event,1000,3000,true); +} - texture_free(&img_laptop_flat_left); - texture_free(&img_laptop_flat_right); +/* Reset Timers */ +void +GameSession::start_timers() +{ + time_left.start(level->timelimit); + Ticks::pause_init(); +} - texture_free(&img_laptop_falling_left); - texture_free(&img_laptop_falling_right); +void +GameSession::on_escape_press() +{ + if(currentsector->player->dying || end_sequence != NO_ENDSEQUENCE) + return; // don't let the player open the menu, when he is dying + + if(game_pause) + return; - for (i = 0; i < 2; i++) + if(st_gl_mode == ST_GL_TEST) { - texture_free(&img_money_left[i]); - texture_free(&img_money_right[i]); + exit_status = ES_LEVEL_ABORT; } - - texture_free(&img_box_full); - texture_free(&img_box_empty); - - texture_free(&img_water); - for (i = 0; i < 3; i++) - texture_free(&img_waves[i]); - - texture_free(&img_pole); - texture_free(&img_poletop); - - for (i = 0; i < 2; i++) - texture_free(&img_flag[i]); - - texture_free(&img_mints); - texture_free(&img_coffee); - - for (i = 0; i < 4; i++) + else if (!Menu::current()) { - texture_free(&img_distro[i]); - texture_free(&img_cloud[0][i]); - texture_free(&img_cloud[1][i]); - } - - texture_free(&img_golden_herring); + /* Tell Tux that the keys are all down, otherwise + it could have nasty bugs, like going allways to the right + or whatever that key does */ + Player& tux = *(currentsector->player); + tux.key_event((SDLKey)keymap.up, UP); + tux.key_event((SDLKey)keymap.down, UP); + tux.key_event((SDLKey)keymap.left, UP); + tux.key_event((SDLKey)keymap.right, UP); + tux.key_event((SDLKey)keymap.jump, UP); + tux.key_event((SDLKey)keymap.power, UP); - for (i = 0; i < NUM_SOUNDS; i++) - free_chunk(sounds[i]); - - /* free the herring song */ - free_music( herring_song ); + Menu::set_current(game_menu); + Ticks::pause_start(); + } } - -/* Draw a tile on the screen: */ - -void drawshape(float x, float y, unsigned int c) +void +GameSession::process_events() { - Tile* ptile = TileManager::instance()->get - (c); - if(ptile) + if (end_sequence != NO_ENDSEQUENCE) { - if(ptile->images.size() > 1) - { - texture_draw(&ptile->images[( ((global_frame_counter*25) / ptile->anim_speed) % (ptile->images.size()))],x,y); - } + Player& tux = *currentsector->player; + + tux.input.fire = UP; + tux.input.left = UP; + tux.input.right = DOWN; + tux.input.down = UP; + + if (int(last_x_pos) == int(tux.get_pos().x)) + tux.input.up = DOWN; else - { - texture_draw(&ptile->images[0],x,y); - } - } + tux.input.up = UP; - /* - if (c == 'X' || c == 'x') - texture_draw(&img_brick[0], x, y); - else if (c == 'Y' || c == 'y') - texture_draw(&img_brick[1], x, y); - else if (c == 'A' || c =='B' || c == '!') - texture_draw(&img_box_full, x, y); - else if (c == 'a') - texture_draw(&img_box_empty, x, y); - else if (c >= 'C' && c <= 'F') - texture_draw(&img_cloud[0][c - 'C'], x, y); - else if (c >= 'c' && c <= 'f') - texture_draw(&img_cloud[1][c - 'c'], x, y); - else if (c >= 'G' && c <= 'J') - texture_draw(&img_bkgd_tile[0][c - 'G'], x, y); - else if (c >= 'g' && c <= 'j') - texture_draw(&img_bkgd_tile[1][c - 'g'], x, y); - else if (c == '#') - texture_draw(&img_solid[0], x, y); - else if (c == '[') - texture_draw(&img_solid[1], x, y); - else if (c == '=') - texture_draw(&img_solid[2], x, y); - else if (c == ']') - texture_draw(&img_solid[3], x, y); - else if (c == '$') - { - z = (global_frame_counter / 2) % 6; - - if (z < 4) - texture_draw(&img_distro[z], x, y); - else if (z == 4) - texture_draw(&img_distro[2], x, y); - else if (z == 5) - texture_draw(&img_distro[1], x, y); - } - else if (c == '^') - { - z = (global_frame_counter / 3) % 3; + last_x_pos = tux.get_pos().x; - texture_draw(&img_waves[z], x, y); - } - else if (c == '*') - texture_draw(&img_poletop, x, y); - else if (c == '|') - { - texture_draw(&img_pole, x, y); + SDL_Event event; + while (SDL_PollEvent(&event)) + { + /* Check for menu-events, if the menu is shown */ + if (Menu::current()) + { + Menu::current()->event(event); + if(!Menu::current()) + Ticks::pause_stop(); + } + switch(event.type) + { + case SDL_QUIT: /* Quit event - quit: */ + Termination::abort("Received window close", ""); + break; + + case SDL_KEYDOWN: /* A keypress! */ + { + SDLKey key = event.key.keysym.sym; + + switch(key) + { + case SDLK_ESCAPE: /* Escape: Open/Close the menu: */ + on_escape_press(); + break; + default: + break; + } + } + + case SDL_JOYBUTTONDOWN: + if (event.jbutton.button == joystick_keymap.start_button) + on_escape_press(); + break; + } + } } - else if (c == '\\') + else // normal mode { - z = (global_frame_counter / 3) % 2; - - texture_draw(&img_flag[z], x + 16, y); - } - else if (c == '&') - texture_draw(&img_water, x, y);*/ - -} + if(!Menu::current() && !game_pause) + Ticks::pause_stop(); + SDL_Event event; + while (SDL_PollEvent(&event)) + { + /* Check for menu-events, if the menu is shown */ + if (Menu::current()) + { + Menu::current()->event(event); + if(!Menu::current()) + Ticks::pause_stop(); + } + else + { + Player& tux = *currentsector->player; + + switch(event.type) + { + case SDL_QUIT: /* Quit event - quit: */ + Termination::abort("Received window close", ""); + break; -/* What shape is at some position? */ -unsigned int shape(float x, float y) -{ + case SDL_KEYDOWN: /* A keypress! */ + { + SDLKey key = event.key.keysym.sym; + + if(tux.key_event(key,DOWN)) + break; + + switch(key) + { + case SDLK_ESCAPE: /* Escape: Open/Close the menu: */ + on_escape_press(); + break; + default: + break; + } + } + break; + case SDL_KEYUP: /* A keyrelease! */ + { + SDLKey key = event.key.keysym.sym; + + if(tux.key_event(key, UP)) + break; + + switch(key) + { + case SDLK_a: + if(debug_mode) + { + char buf[160]; + snprintf(buf, sizeof(buf), "P: %4.1f,%4.1f", + tux.get_pos().x, tux.get_pos().y); + context->draw_text(white_text, buf, + Vector(0, screen->h - white_text->get_height()), + LEFT_ALLIGN, LAYER_FOREGROUND1); + context->do_drawing(); + SDL_Delay(1000); + } + break; + case SDLK_p: + if(!Menu::current()) + { + // "lifeup" cheat activates pause cause of the 'p' + // so work around to ignore it + if(compare_last(last_keys, "lifeu")) + break; + + if(game_pause) + { + game_pause = false; + Ticks::pause_stop(); + } + else + { + game_pause = true; + Ticks::pause_start(); + } + } + break; + default: + break; + } + } + + /* Check if chacrater is ASCII */ + char ch[2]; + if((event.key.keysym.unicode & 0xFF80) == 0) + { + ch[0] = event.key.keysym.unicode & 0x7F; + ch[1] = '\0'; + } + last_keys.append(ch); // add to cheat keys + + // Cheating words (the goal of this is really for debugging, + // but could be used for some cheating, nothing wrong with that) + if(compare_last(last_keys, "grow")) + { + tux.grow(false); + last_keys.clear(); + } + if(compare_last(last_keys, "fire")) + { + tux.grow(false); + tux.got_power = tux.FIRE_POWER; + last_keys.clear(); + } + if(compare_last(last_keys, "ice")) + { + tux.grow(false); + tux.got_power = tux.ICE_POWER; + last_keys.clear(); + } + if(compare_last(last_keys, "lifeup")) + { + player_status.lives++; + last_keys.clear(); + } + if(compare_last(last_keys, "lifedown")) + { + player_status.lives--; + last_keys.clear(); + } + if(compare_last(last_keys, "grease")) + { + tux.physic.set_velocity_x(tux.physic.get_velocity_x()*3); + last_keys.clear(); + } + if(compare_last(last_keys, "invincible")) + { // be invincle for the rest of the level + tux.invincible_timer.start(10000); + last_keys.clear(); + } + if(compare_last(last_keys, "shrink")) + { // remove powerups + tux.kill(tux.SHRINK); + last_keys.clear(); + } + if(compare_last(last_keys, "kill")) + { // kill Tux, but without losing a life + player_status.lives++; + tux.kill(tux.KILL); + last_keys.clear(); + } + if(compare_last(last_keys, "grid")) + { // toggle debug grid + debug_grid = !debug_grid; + last_keys.clear(); + } + if(compare_last(last_keys, "hover")) + { // toggle hover ability on/off + tux.enable_hover = !tux.enable_hover; + last_keys.clear(); + } + if(compare_last(last_keys, "gotoend")) + { // goes to the end of the level + tux.move(Vector( + (currentsector->solids->get_width()*32) + - (screen->w*2), + 0)); + currentsector->camera->reset( + Vector(tux.get_pos().x, tux.get_pos().y)); + last_keys.clear(); + } + // temporary to help player's choosing a flapping + if(compare_last(last_keys, "marek")) + { + tux.flapping_mode = Player::MAREK_FLAP; + last_keys.clear(); + } + if(compare_last(last_keys, "ricardo")) + { + tux.flapping_mode = Player::RICARDO_FLAP; + last_keys.clear(); + } + if(compare_last(last_keys, "ryan")) + { + tux.flapping_mode = Player::RYAN_FLAP; + last_keys.clear(); + } + break; - int xx, yy; - unsigned int c; + case SDL_JOYAXISMOTION: + if (event.jaxis.axis == joystick_keymap.x_axis) + { + if (event.jaxis.value < -joystick_keymap.dead_zone) + { + tux.input.left = DOWN; + tux.input.right = UP; + } + else if (event.jaxis.value > joystick_keymap.dead_zone) + { + tux.input.left = UP; + tux.input.right = DOWN; + } + else + { + tux.input.left = DOWN; + tux.input.right = DOWN; + } + } + else if (event.jaxis.axis == joystick_keymap.y_axis) + { + if (event.jaxis.value > joystick_keymap.dead_zone) + { + tux.input.up = DOWN; + tux.input.down = UP; + } + else if (event.jaxis.value < -joystick_keymap.dead_zone) + { + tux.input.up = UP; + tux.input.down = DOWN; + } + else + { + tux.input.up = DOWN; + tux.input.down = DOWN; + } + } + break; - yy = ((int)y / 32); - xx = ((int)x / 32); + case SDL_JOYHATMOTION: + if(event.jhat.value & SDL_HAT_UP) { + tux.input.up = DOWN; + tux.input.down = UP; + } else if(event.jhat.value & SDL_HAT_DOWN) { + tux.input.up = UP; + tux.input.down = DOWN; + } else if(event.jhat.value & SDL_HAT_LEFT) { + tux.input.left = DOWN; + tux.input.right = UP; + } else if(event.jhat.value & SDL_HAT_RIGHT) { + tux.input.left = UP; + tux.input.right = DOWN; + } else if(event.jhat.value == SDL_HAT_CENTERED) { + tux.input.left = UP; + tux.input.right = UP; + tux.input.up = UP; + tux.input.down = UP; + } + break; + + case SDL_JOYBUTTONDOWN: + if (event.jbutton.button == joystick_keymap.a_button) + tux.input.jump = DOWN; + else if (event.jbutton.button == joystick_keymap.b_button) + tux.input.fire = DOWN; + else if (event.jbutton.button == joystick_keymap.start_button) + on_escape_press(); + break; + case SDL_JOYBUTTONUP: + if (event.jbutton.button == joystick_keymap.a_button) + tux.input.jump = UP; + else if (event.jbutton.button == joystick_keymap.b_button) + tux.input.fire = UP; + break; - if (yy >= 0 && yy < 15 && xx >= 0 && xx <= current_level.width) - { - c = current_level.ia_tiles[yy][xx]; + default: + break; + } /* switch */ + } + } /* while */ } - else - c = '.'; - - return(c); } -Tile* gettile(float x, float y) +void +GameSession::check_end_conditions() { - return TileManager::instance()->get(shape(x, y)); + Player* tux = currentsector->player; + + /* End of level? */ + if(end_sequence && endsequence_timer.check()) { + exit_status = ES_LEVEL_FINISHED; + global_stats += last_swap_stats; // add swap points stats + return; + } else if (!end_sequence && tux->is_dead()) { + player_status.bonus = PlayerStatus::NO_BONUS; + + if (player_status.lives < 0) { // No more lives!? + exit_status = ES_GAME_OVER; + } else { // Still has lives, so reset Tux to the levelstart + restart_level(); + } + + return; + } } -/* Is is ground? */ - - -bool issolid(float x, float y) +void +GameSession::action(float elapsed_time) { - Tile* tile = TileManager::instance()->get - (shape(x,y)); - if(tile) - { - if(tile->solid == true) - return true; - else - return false; - } - else + // advance timers + if (exit_status == ES_NONE && !currentsector->player->growing_timer.check()) { - return false; + // Update Tux and the World + currentsector->action(elapsed_time); } -} -/* Is it a brick? */ - -bool isbrick(float x, float y) -{ - Tile* tile = TileManager::instance()->get - (shape(x,y)); - if(tile) - { - if(tile->brick == true) - return true; - else - return false; - } - else - { - return false; - } + // respawning in new sector? + if(newsector != "" && newspawnpoint != "") { + Sector* sector = level->get_sector(newsector); + currentsector = sector; + currentsector->activate(newspawnpoint); + currentsector->play_music(LEVEL_MUSIC); + newsector = newspawnpoint = ""; + } } - -/* Is it ice? */ - -bool isice(float x, float y) +void +GameSession::draw() { - Tile* tile = TileManager::instance()->get - (shape(x,y)); - if(tile) - { - if(tile->ice == true) - return true; - else - return false; - } - else + currentsector->draw(*context); + drawstatus(*context); + + if(game_pause) { - return false; - } -} + int x = screen->h / 20; + for(int i = 0; i < x; ++i) + { + context->draw_filled_rect( + Vector(i % 2 ? (pause_menu_frame * i)%screen->w : + -((pause_menu_frame * i)%screen->w) + ,(i*20+pause_menu_frame)%screen->h), + Vector(screen->w,10), + Color(20,20,20, rand() % 20 + 1), LAYER_FOREGROUND1+1); + } + context->draw_filled_rect( + Vector(0,0), Vector(screen->w, screen->h), + Color(rand() % 50, rand() % 50, rand() % 50, 128), LAYER_FOREGROUND1); + context->draw_text(blue_text, _("PAUSE - Press 'P' To Play"), + Vector(screen->w/2, 230), CENTER_ALLIGN, LAYER_FOREGROUND1+2); -/* Is it a full box? */ + char str1[60]; + char str2[124]; + sprintf(str1, _("Playing: ")); + sprintf(str2, level->name.c_str()); -bool isfullbox(float x, float y) -{ - Tile* tile = TileManager::instance()->get - (shape(x,y)); - if(tile) - { - if(tile->fullbox == true) - return true; - else - return false; + context->draw_text(blue_text, str1, + Vector((screen->w - (blue_text->get_text_width(str1) + white_text->get_text_width(str2)))/2, 340), + LEFT_ALLIGN, LAYER_FOREGROUND1+2); + context->draw_text(white_text, str2, + Vector(((screen->w - (blue_text->get_text_width(str1) + white_text->get_text_width(str2)))/2)+blue_text->get_text_width(str1), 340), + LEFT_ALLIGN, LAYER_FOREGROUND1+2); } - else + + if(Menu::current()) { - return false; + Menu::current()->draw(*context); + mouse_cursor->draw(*context); } -} -bool isdistro(float x, float y) -{ - Tile* tile = TileManager::instance()->get(shape(x,y)); - return tile && tile->distro; + context->do_drawing(); } -/* Break a brick: */ - -void trybreakbrick(float x, float y) +void +GameSession::process_menu() { - if (isbrick(x, y)) + Menu* menu = Menu::current(); + if(menu) { - if (shape(x, y) == 'x' || shape(x, y) == 'y') - { - /* Get a distro from it: */ + menu->action(); - add_bouncy_distro(((int)(x + 1) / 32) * 32, - (int)(y / 32) * 32); - - if (!counting_distros) + if(menu == game_menu) + { + switch (game_menu->check()) { - counting_distros = true; - distro_counter = 50; + case MNID_CONTINUE: + Ticks::pause_stop(); + break; + case MNID_ABORTLEVEL: + Ticks::pause_stop(); + exit_status = ES_LEVEL_ABORT; + break; } - - if (distro_counter <= 0) - level_change(¤t_level,x, y, TM_IA, 'a'); - - play_sound(sounds[SND_DISTRO], SOUND_CENTER_SPEAKER); - score = score + SCORE_DISTRO; - distros++; } - else + else if(menu == options_menu) { - /* Get rid of it: */ - - level_change(¤t_level,x, y, TM_IA, '.'); + process_options_menu(); + } + else if(menu == load_game_menu ) + { + process_load_game_menu(); } - - - /* Replace it with broken bits: */ - - add_broken_brick(((int)(x + 1) / 32) * 32, - (int)(y / 32) * 32); - - - /* Get some score: */ - - play_sound(sounds[SND_BRICK], SOUND_CENTER_SPEAKER); - score = score + SCORE_BRICK; } } - -/* Bounce a brick: */ - -void bumpbrick(float x, float y) +GameSession::ExitStatus +GameSession::run() { - add_bouncy_brick(((int)(x + 1) / 32) * 32, - (int)(y / 32) * 32); - - play_sound(sounds[SND_BRICK], SOUND_CENTER_SPEAKER); + Menu::set_current(0); + current_ = this; + + int fps_cnt = 0; -} + // Eat unneeded events + SDL_Event event; + while(SDL_PollEvent(&event)) + {} + draw(); -/* Empty a box: */ + Uint32 lastticks = SDL_GetTicks(); + fps_ticks = SDL_GetTicks(); -void tryemptybox(float x, float y, int col_side) -{ - if (!isfullbox(x, y)) - return; + while (exit_status == ES_NONE) { + Uint32 ticks = SDL_GetTicks(); + float elapsed_time = float(ticks - lastticks) / 1000.; + if(!game_pause) + global_time += elapsed_time; + lastticks = ticks; - // according to the collision side, set the upgrade direction + // 40fps is minimum + if(elapsed_time > .025) + elapsed_time = .025; + + /* Handle events: */ + currentsector->player->input.old_fire = currentsector->player->input.fire; + currentsector->player->input.old_up = currentsector->player->input.old_up; - if(col_side == LEFT) - col_side = RIGHT; - else - col_side = LEFT; + process_events(); + process_menu(); - // FIXME: Content of boxes must be handled otherwise - switch(gettile(x,y)->data) + // Update the world state and all objects in the world + // Do that with a constante time-delta so that the game will run + // determistic and not different on different machines + if(!game_pause && !Menu::current()) { - case 1: //'A': /* Box with a distro! */ - add_bouncy_distro(((int)(x + 1) / 32) * 32, (int)(y / 32) * 32 - 32); - play_sound(sounds[SND_DISTRO], SOUND_CENTER_SPEAKER); - score = score + SCORE_DISTRO; - distros++; - break; - case 2: // 'B': /* Add an upgrade! */ - if (tux.size == SMALL) /* Tux is small, add mints! */ - add_upgrade((int)((x + 1) / 32) * 32, (int)(y / 32) * 32 - 32, col_side, UPGRADE_MINTS); - else /* Tux is big, add coffee: */ - add_upgrade((int)((x + 1) / 32) * 32, (int)(y / 32) * 32 - 32, col_side, UPGRADE_COFFEE); - play_sound(sounds[SND_UPGRADE], SOUND_CENTER_SPEAKER); - break; - case 3:// '!': /* Add a golden herring */ - add_upgrade((int)((x + 1) / 32) * 32, (int)(y / 32) * 32 - 32, col_side, UPGRADE_HERRING); - break; - default: - break; + // Update the world + check_end_conditions(); + if (end_sequence == ENDSEQUENCE_RUNNING) + action(elapsed_time/2); + else if(end_sequence == NO_ENDSEQUENCE) + action(elapsed_time); } - - /* Empty the box: */ - level_change(¤t_level,x, y, TM_IA, 'a'); -} - - -/* Try to grab a distro: */ - -void trygrabdistro(float x, float y, int bounciness) -{ - Tile* tile = gettile(x, y); - if (tile && tile->distro) + else { - level_change(¤t_level,x, y, TM_IA, '.'); - play_sound(sounds[SND_DISTRO], SOUND_CENTER_SPEAKER); - - if (bounciness == BOUNCE) - { - add_bouncy_distro(((int)(x + 1) / 32) * 32, - (int)(y / 32) * 32); - } - - score = score + SCORE_DISTRO; - distros++; + ++pause_menu_frame; + SDL_Delay(50); } -} -/* Try to bump a bad guy from below: */ - -void trybumpbadguy(float x, float y) -{ - unsigned int i; + draw(); - /* Bad guys: */ - for (i = 0; i < bad_guys.size(); i++) + /* Time stops in pause mode */ + if(game_pause || Menu::current()) { - if (bad_guys[i].base.x >= x - 32 && bad_guys[i].base.x <= x + 32 && - bad_guys[i].base.y >= y - 16 && bad_guys[i].base.y <= y + 16) - { - if (bad_guys[i].kind == BAD_BSOD || - bad_guys[i].kind == BAD_LAPTOP) - { - bad_guys[i].dying = DYING_FALLING; - bad_guys[i].base.ym = -8; - play_sound(sounds[SND_FALL], SOUND_CENTER_SPEAKER); - } - } + continue; } + //frame_rate.update(); + + /* Handle time: */ + if (time_left.check() && !end_sequence) + currentsector->player->kill(Player::KILL); + + /* Handle music: */ + if (currentsector->player->invincible_timer.started() && !end_sequence) + { + currentsector->play_music(HERRING_MUSIC); + } + /* are we low on time ? */ + else if (time_left.get_timeleft() < TIME_WARNING && !end_sequence) + { + currentsector->play_music(HURRYUP_MUSIC); + } + /* or just normal music? */ + else if(currentsector->get_music_type() != LEVEL_MUSIC && !end_sequence) + { + currentsector->play_music(LEVEL_MUSIC); + } - /* Upgrades: */ - for (i = 0; i < upgrades.size(); i++) + /* Calculate frames per second */ + if(show_fps) { - if (upgrades[i].base.height == 32 && - upgrades[i].base.x >= x - 32 && upgrades[i].base.x <= x + 32 && - upgrades[i].base.y >= y - 16 && upgrades[i].base.y <= y + 16) - { - upgrades[i].base.xm = -upgrades[i].base.xm; - upgrades[i].base.ym = -8; - play_sound(sounds[SND_BUMP_UPGRADE], SOUND_CENTER_SPEAKER); - } + ++fps_cnt; + + if(SDL_GetTicks() - fps_ticks >= 500) + { + fps_fps = (float) fps_cnt / .5; + fps_cnt = 0; + fps_ticks = SDL_GetTicks(); + } } + } + + return exit_status; } -/* (Status): */ -void drawstatus(void) +void +GameSession::respawn(const std::string& sector, const std::string& spawnpoint) { - char str[60]; + newsector = sector; + newspawnpoint = spawnpoint; +} - sprintf(str, "%d", score); - text_draw(&white_text, "SCORE", 0, 0, 1); - text_draw(&gold_text, str, 96, 0, 1); +void +GameSession::start_sequence(const std::string& sequencename) +{ + if(sequencename == "endsequence") { + if(end_sequence) + return; + + end_sequence = ENDSEQUENCE_RUNNING; + endsequence_timer.start(7.0); // 7 seconds until we finish the map + last_x_pos = -1; + SoundManager::get()->play_music(level_end_song, 0); + currentsector->player->invincible_timer.start(7.0); + + // add left time to stats + global_stats.set_points(TIME_NEEDED_STAT, + int(time_left.get_period() - time_left.get_timeleft())); + + if(level->get_end_sequence_type() == Level::FIREWORKS_ENDSEQ_ANIM) { + currentsector->add_object(new Fireworks()); + } + } else { + std::cout << "Unknown sequence '" << sequencename << "'.\n"; + } +} - if(st_gl_mode != ST_GL_TEST) - { - sprintf(str, "%d", hs_score); - text_draw(&white_text, "HIGH", 0, 20, 1); - text_draw(&gold_text, str, 96, 20, 1); +/* (Status): */ +void +GameSession::drawstatus(DrawingContext& context) +{ + char str[60]; + + snprintf(str, 60, " %d", global_stats.get_points(SCORE_STAT)); + context.draw_text(white_text, _("SCORE"), Vector(0, 0), LEFT_ALLIGN, LAYER_FOREGROUND1); + context.draw_text(gold_text, str, Vector(96, 0), LEFT_ALLIGN, LAYER_FOREGROUND1); + + if(st_gl_mode == ST_GL_TEST) + { + context.draw_text(white_text, _("Press ESC To Return"), Vector(0,20), + LEFT_ALLIGN, LAYER_FOREGROUND1); + } + + if(time_left.get_timeleft() < 0) { + context.draw_text(white_text, _("TIME's UP"), Vector(screen->w/2, 0), + CENTER_ALLIGN, LAYER_FOREGROUND1); + } else if (time_left.get_timeleft() > TIME_WARNING + || int(global_time * 2.5) % 2) { + sprintf(str, " %d", int(time_left.get_timeleft())); + context.draw_text(white_text, _("TIME"), + Vector(screen->w/2, 0), CENTER_ALLIGN, LAYER_FOREGROUND1); + context.draw_text(gold_text, str, + Vector(screen->w/2 + 4*16, 0), CENTER_ALLIGN, LAYER_FOREGROUND1); + } + + sprintf(str, " %d", player_status.distros); + context.draw_text(white_text, _("COINS"), + Vector(screen->w - white_text->get_text_width(_("COINS"))-white_text->get_text_width(" 99"), 0), + LEFT_ALLIGN, LAYER_FOREGROUND1); + context.draw_text(gold_text, str, + Vector(screen->w - gold_text->get_text_width(" 99"), 0),LEFT_ALLIGN, LAYER_FOREGROUND1); + + if (player_status.lives >= 5) + { + sprintf(str, "%dx", player_status.lives); + float x = screen->w - gold_text->get_text_width(str) - tux_life->w; + context.draw_text(gold_text, str, Vector(x, 20), LEFT_ALLIGN, LAYER_FOREGROUND1); + context.draw_surface(tux_life, Vector(screen->w - 16, 20), + LAYER_FOREGROUND1); } else { - text_draw(&white_text,"Press ESC To Return",0,20,1); - } - - if (timer_get_left(&time_left) > TIME_WARNING || (global_frame_counter % 10) < 5) - { - sprintf(str, "%d", timer_get_left(&time_left) / 1000 ); - text_draw(&white_text, "TIME", 224, 0, 1); - text_draw(&gold_text, str, 304, 0, 1); + for(int i= 0; i < player_status.lives; ++i) + context.draw_surface(tux_life, + Vector(screen->w - tux_life->w*4 +(tux_life->w*i), 20), + LAYER_FOREGROUND1); } - sprintf(str, "%d", distros); - text_draw(&white_text, "DISTROS", screen->h, 0, 1); - text_draw(&gold_text, str, 608, 0, 1); - - text_draw(&white_text, "LIVES", screen->h, 20, 1); + context.draw_text(white_text, _("LIVES"), + Vector(screen->w - white_text->get_text_width(_("LIVES")) - white_text->get_text_width(" 99"), 20), + LEFT_ALLIGN, LAYER_FOREGROUND1); if(show_fps) { sprintf(str, "%2.1f", fps_fps); - text_draw(&white_text, "FPS", screen->h, 40, 1); - text_draw(&gold_text, str, screen->h + 60, 40, 1); - } - - for(int i=0; i < tux.lives; ++i) - { - texture_draw(&tux_life,565+(18*i),20); + context.draw_text(white_text, "FPS", + Vector(screen->w - white_text->get_text_width("FPS "), 40), + LEFT_ALLIGN, LAYER_FOREGROUND1); + context.draw_text(gold_text, str, + Vector(screen->w-4*16, 40), LEFT_ALLIGN, LAYER_FOREGROUND1); } } - -void drawendscreen(void) -{ - char str[80]; - - clearscreen(0, 0, 0); - - text_drawf(&blue_text, "GAMEOVER", 0, 200, A_HMIDDLE, A_TOP, 1); - - sprintf(str, "SCORE: %d", score); - text_drawf(&gold_text, str, 0, 224, A_HMIDDLE, A_TOP, 1); - - sprintf(str, "DISTROS: %d", distros); - text_drawf(&gold_text, str, 0, 256, A_HMIDDLE, A_TOP, 1); - - flipscreen(); - SDL_Delay(2000); -} - -void drawresultscreen(void) +void +GameSession::drawresultscreen() { char str[80]; - clearscreen(0, 0, 0); + DrawingContext context; + for(Sector::GameObjects::iterator i = currentsector->gameobjects.begin(); + i != currentsector->gameobjects.end(); ++i) { + Background* background = dynamic_cast (*i); + if(background) { + background->draw(context); + } + } - text_drawf(&blue_text, "Result:", 0, 200, A_HMIDDLE, A_TOP, 1); + context.draw_text(blue_text, _("Result:"), Vector(screen->w/2, 200), + CENTER_ALLIGN, LAYER_FOREGROUND1); - sprintf(str, "SCORE: %d", score); - text_drawf(&gold_text, str, 0, 224, A_HMIDDLE, A_TOP, 1); + sprintf(str, _("SCORE: %d"), global_stats.get_points(SCORE_STAT)); + context.draw_text(gold_text, str, Vector(screen->w/2, 224), CENTER_ALLIGN, LAYER_FOREGROUND1); - sprintf(str, "DISTROS: %d", distros); - text_drawf(&gold_text, str, 0, 256, A_HMIDDLE, A_TOP, 1); + sprintf(str, _("COINS: %d"), player_status.distros); + context.draw_text(gold_text, str, Vector(screen->w/2, 256), CENTER_ALLIGN, LAYER_FOREGROUND1); - flipscreen(); - SDL_Delay(2000); + context.do_drawing(); + + SDL_Event event; + wait_for_event(event,2000,5000,true); } -void savegame(int slot) +std::string slotinfo(int slot) { - char savefile[1024]; - FILE* fi; - unsigned int ui; - - sprintf(savefile,"%s/slot%d.save",st_save_dir,slot); - - fi = fopen(savefile, "wb"); - - if (fi == NULL) - { - fprintf(stderr, "Warning: I could not open the slot file "); - - } - else - { - fputs(level_subset, fi); - fputs("\n", fi); - fwrite(&level,sizeof(int),1,fi); - fwrite(&score,sizeof(int),1,fi); - fwrite(&distros,sizeof(int),1,fi); - fwrite(&scroll_x,sizeof(float),1,fi); - fwrite(&tux,sizeof(Player),1,fi); - timer_fwrite(&tux.invincible_timer,fi); - timer_fwrite(&tux.skidding_timer,fi); - timer_fwrite(&tux.safe_timer,fi); - timer_fwrite(&tux.frame_timer,fi); - timer_fwrite(&time_left,fi); - ui = st_get_ticks(); - fwrite(&ui,sizeof(int),1,fi); - } - fclose(fi); - + std::string tmp; + std::string slotfile; + std::string title; + std::stringstream stream; + stream << slot; + slotfile = st_save_dir + "/slot" + stream.str() + ".stsg"; + + try { + lisp::Parser parser; + std::auto_ptr root (parser.parse(slotfile)); + + const lisp::Lisp* savegame = root->get_lisp("supertux-savegame"); + if(!savegame) + throw std::runtime_error("file is not a supertux-savegame."); + + savegame->get("title", title); + } catch(std::exception& e) { + return std::string(_("Slot")) + " " + stream.str() + " - " + + std::string(_("Free")); + } + + return std::string("Slot ") + stream.str() + " - " + title; } -void loadgame(int slot) +bool process_load_game_menu() { - char savefile[1024]; - char str[100]; - FILE* fi; - unsigned int ui; + int slot = load_game_menu->check(); - sprintf(savefile,"%s/slot%d.save",st_save_dir,slot); - - fi = fopen(savefile, "rb"); - - if (fi == NULL) + if(slot != -1 && load_game_menu->get_item_by_id(slot).kind == MN_ACTION) { - fprintf(stderr, "Warning: I could not open the slot file "); + std::stringstream stream; + stream << slot; + std::string slotfile = st_save_dir + "/slot" + stream.str() + ".stsg"; - } - else - { - fgets(str, 100, fi); - strcpy(level_subset, str); - level_subset[strlen(level_subset)-1] = '\0'; - fread(&level,sizeof(int),1,fi); - - set_defaults(); - level_free(¤t_level); - if(level_load(¤t_level,level_subset,level) != 0) - exit(1); - arrays_free(); - arrays_init(); - activate_bad_guys(); - activate_particle_systems(); - level_free_gfx(); - level_load_gfx(¤t_level); - level_free_song(); - level_load_song(¤t_level); - levelintro(); - update_time = st_get_ticks(); - - fread(&score,sizeof(int),1,fi); - fread(&distros,sizeof(int),1,fi); - fread(&scroll_x,sizeof(float),1,fi); - fread(&tux, sizeof(Player), 1, fi); - timer_fread(&tux.invincible_timer,fi); - timer_fread(&tux.skidding_timer,fi); - timer_fread(&tux.safe_timer,fi); - timer_fread(&tux.frame_timer,fi); - timer_fread(&time_left,fi); - fread(&ui,sizeof(int),1,fi); - tux.hphysic.start_time += st_get_ticks() - ui; - tux.vphysic.start_time += st_get_ticks() - ui; - fclose(fi); - } + fadeout(256); + DrawingContext context; + context.draw_text(white_text, "Loading...", + Vector(screen->w/2, screen->h/2), CENTER_ALLIGN, LAYER_FOREGROUND1); + context.do_drawing(); -} + WorldMapNS::WorldMap worldmap; -void slotinfo(char **pinfo, int slot) -{ - FILE* fi; - char slotfile[1024]; - char tmp[200]; - char str[5]; - int slot_level; - sprintf(slotfile,"%s/slot%d.save",st_save_dir,slot); + worldmap.set_map_filename("/levels/world1/worldmap.stwm"); + // Load the game or at least set the savegame_file variable + worldmap.loadgame(slotfile); - fi = fopen(slotfile, "rb"); + worldmap.display(); - sprintf(tmp,"Slot %d - ",slot); + Menu::set_current(main_menu); - if (fi == NULL) - { - strcat(tmp,"Free"); + Ticks::pause_stop(); + return true; } else { - fgets(str, 100, fi); - str[strlen(str)-1] = '\0'; - strcat(tmp, str); - strcat(tmp, " / Level:"); - fread(&slot_level,sizeof(int),1,fi); - sprintf(str,"%d",slot_level); - strcat(tmp,str); - fclose(fi); + return false; } - - *pinfo = (char*) malloc(sizeof(char) * (strlen(tmp)+1)); - strcpy(*pinfo,tmp); } -