X-Git-Url: https://git.verplant.org/?a=blobdiff_plain;f=src%2Fgameloop.cpp;h=275c42fa62190be20265986908a69430a560d051;hb=32c639e36abc3b7bd99380c8b5da768a127fafd6;hp=68d986924f60d41941c6f3c3212e0f7e5c6c4519;hpb=7adc9ba6b26922c1d41443d6a5f2da324c83db2d;p=supertux.git diff --git a/src/gameloop.cpp b/src/gameloop.cpp index 68d986924..275c42fa6 100644 --- a/src/gameloop.cpp +++ b/src/gameloop.cpp @@ -1,25 +1,38 @@ -/* - 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 +// $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 -#include +#include +#include + +#include "SDL.h" #ifndef WIN32 #include @@ -27,128 +40,175 @@ #endif #include "defines.h" -#include "globals.h" +#include "app/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 "video/screen.h" +#include "app/setup.h" +#include "gui/menu.h" +#include "sector.h" #include "level.h" #include "scene.h" -#include "collision.h" #include "tile.h" -#include "particlesystem.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; -void -GameSession::init() +bool compare_last(std::string& haystack, std::string needle) { - game_pause = false; -} + int haystack_size = haystack.size(); + int needle_size = needle.size(); -GameSession::GameSession() -{ - current_ = this; - assert(0); + if(haystack_size < needle_size) + return false; + + if(haystack.compare(haystack_size-needle_size, needle_size, needle) == 0) + return true; + return false; } -GameSession::GameSession(const std::string& filename) +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) { - init(); - - //assert(!"Don't call me"); current_ = this; + + game_pause = false; + fps_fps = 0; - world = new World; + context = new DrawingContext(); - fps_timer.init(true); - frame_timer.init(true); + last_swap_point = Vector(-1, -1); + last_swap_stats.reset(); - world->load(filename); + restart_level(); } -GameSession::GameSession(const std::string& subset_, int levelnb_, int mode) - : subset(subset_), - levelnb(levelnb_) +void +GameSession::restart_level() { - init(); + game_pause = false; + exit_status = ES_NONE; + end_sequence = NO_ENDSEQUENCE; - current_ = this; + last_keys.clear(); - world = new World; +#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; - fps_timer.init(true); - frame_timer.init(true); + level = new Level; + level->load(levelfile); - st_gl_mode = mode; - - /* Init the game: */ - world->arrays_free(); - world->set_defaults(); + 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); - if (st_gl_mode == ST_GL_LOAD_LEVEL_FILE) - { - if (world->load(subset)) - exit(1); - } - else + 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) { - if(world->load(subset, levelnb) != 0) - exit(1); - } + tux_pos = currentsector->get_best_spawn_point(tux_pos); - world->get_level()->load_gfx(); - - world->activate_bad_guys(); - world->activate_particle_systems(); - world->get_level()->load_song(); + if(last_swap_point.x > tux_pos.x) + tux_pos = last_swap_point; + else // new swap point + { + last_swap_point = tux_pos; - if(st_gl_mode != ST_GL_TEST) - load_hs(); + last_swap_stats += global_stats; + } - if(st_gl_mode == ST_GL_PLAY || st_gl_mode == ST_GL_LOAD_LEVEL_FILE) - levelintro(); + currentsector->player->base.x = tux_pos.x; + currentsector->player->base.y = tux_pos.y; - time_left.init(true); - start_timers(); + // has to reset camera on swapping + currentsector->camera->reset(Vector(currentsector->player->base.x, + currentsector->player->base.y)); + } +#endif + + if (st_gl_mode != ST_GL_DEMO_GAME) + { + if(st_gl_mode == ST_GL_PLAY || st_gl_mode == ST_GL_LOAD_LEVEL_FILE) + levelintro(); + } - if(st_gl_mode == ST_GL_LOAD_GAME) - loadgame(levelnb); + start_timers(); + currentsector->play_music(LEVEL_MUSIC); } GameSession::~GameSession() { - delete world; + delete level; + delete context; } void GameSession::levelintro(void) { - Player& tux = *world->get_tux(); - + SoundManager::get()->halt_music(); + char str[60]; - /* Level Intro: */ - clearscreen(0, 0, 0); - sprintf(str, "LEVEL %d", levelnb); - blue_text->drawf(str, 0, 200, A_HMIDDLE, A_TOP, 1); + 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); + } + } - sprintf(str, "%s", world->get_level()->name.c_str()); - gold_text->drawf(str, 0, 224, A_HMIDDLE, A_TOP, 1); +// 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); - sprintf(str, "by %s", world->get_level()->author.c_str()); - red_text->drawf(str, 0, 256, A_HMIDDLE, A_TOP, 1); - - sprintf(str, "TUX x %d", tux.lives); - white_text->drawf(str, 0, 288, A_HMIDDLE, A_TOP, 1); + sprintf(str, "TUX x %d", player_status.lives); + context.draw_text(white_text, str, Vector(screen->w/2, 210), + CENTER_ALLIGN, LAYER_FOREGROUND1); - flipscreen(); + 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); + + + if(best_level_statistics != NULL) + best_level_statistics->draw_message_info(context, _("Best Level Statistics")); + + context.do_drawing(); SDL_Event event; wait_for_event(event,1000,3000,true); @@ -158,715 +218,773 @@ GameSession::levelintro(void) void GameSession::start_timers() { - time_left.start(world->get_level()->time_left*1000); - st_pause_ticks_init(); - update_time = st_get_ticks(); + time_left.start(level->timelimit); + Ticks::pause_init(); } void -GameSession::process_events() +GameSession::on_escape_press() { - Player& tux = *world->get_tux(); + 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; - SDL_Event event; - while (SDL_PollEvent(&event)) + if(st_gl_mode == ST_GL_TEST) + { + exit_status = ES_LEVEL_ABORT; + } + else if (!Menu::current()) { - /* Check for menu-events, if the menu is shown */ - if(show_menu) - current_menu->event(event); + /* 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); + + Menu::set_current(game_menu); + Ticks::pause_start(); + } +} - switch(event.type) - { - case SDL_QUIT: /* Quit event - quit: */ - quit = true; - break; - case SDL_KEYDOWN: /* A keypress! */ - { - SDLKey key = event.key.keysym.sym; - - if(tux.key_event(key,DOWN)) - break; +void +GameSession::process_events() +{ + if (end_sequence != NO_ENDSEQUENCE) + { + 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 + tux.input.up = UP; - switch(key) - { - case SDLK_ESCAPE: /* Escape: Open/Close the menu: */ - if(!game_pause) - { - if(st_gl_mode == ST_GL_TEST) - quit = true; - 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! */ - { - SDLKey key = event.key.keysym.sym; + last_x_pos = tux.get_pos().x; - if(tux.key_event(key, UP)) - break; + 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(key) + switch(event.type) + { + case SDL_QUIT: /* Quit event - quit: */ + Termination::abort("Received window close", ""); + break; + + case SDL_KEYDOWN: /* A keypress! */ { - case SDLK_p: - if(!show_menu) - { - if(game_pause) - { - game_pause = false; - st_pause_ticks_stop(); - } - else - { - game_pause = true; - st_pause_ticks_start(); - } - } - break; - case SDLK_TAB: - if(debug_mode) + SDLKey key = event.key.keysym.sym; + + switch(key) { - tux.size = !tux.size; - if(tux.size == BIG) - { - tux.base.height = 64; - } - else - tux.base.height = 32; + case SDLK_ESCAPE: /* Escape: Open/Close the menu: */ + on_escape_press(); + break; + default: + break; } - break; - case SDLK_END: - if(debug_mode) - player_status.distros += 50; - break; - case SDLK_SPACE: - if(debug_mode) - player_status.next_level = 1; - break; - case SDLK_DELETE: - if(debug_mode) - tux.got_coffee = 1; - break; - case SDLK_INSERT: - if(debug_mode) - tux.invincible_timer.start(TUX_INVINCIBLE_TIME); - break; - case SDLK_l: - if(debug_mode) - --tux.lives; - break; - case SDLK_s: - if(debug_mode) - player_status.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; - - break; - default: + + case SDL_JOYBUTTONDOWN: + if (event.jbutton.button == joystick_keymap.start_button) + on_escape_press(); 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; - - break; - - default: - break; - - } /* switch */ - - } /* while */ -} - -int -GameSession::action(double frame_ratio) -{ - Player& tux = *world->get_tux(); - - if (tux.is_dead() || player_status.next_level) + } + } + else // normal mode { - /* Tux either died, or reached the end of a level! */ - halt_music(); - - if (player_status.next_level) + if(!Menu::current() && !game_pause) + Ticks::pause_stop(); + + SDL_Event event; + while (SDL_PollEvent(&event)) { - /* End of a level! */ - levelnb++; - player_status.next_level = 0; - if(st_gl_mode != ST_GL_TEST) + /* Check for menu-events, if the menu is shown */ + if (Menu::current()) { - drawresultscreen(); + Menu::current()->event(event); + if(!Menu::current()) + Ticks::pause_stop(); } else { - world->get_level()->free_gfx(); - world->get_level()->cleanup(); - world->get_level()->free_song(); - world->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) + Player& tux = *currentsector->player; + + switch(event.type) { - if (player_status.score > hs_score) - save_hs(player_status.score); - } + case SDL_QUIT: /* Quit event - quit: */ + Termination::abort("Received window close", ""); + break; - world->get_level()->free_gfx(); - world->get_level()->cleanup(); - world->get_level()->free_song(); - world->arrays_free(); + case SDL_KEYDOWN: /* A keypress! */ + { + SDLKey key = event.key.keysym.sym; + + if(tux.key_event(key,DOWN)) + break; - return(0); - } /* if (lives < 0) */ - } + 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; - /* Either way, (re-)load the (next) level... */ - tux.level_begin(); - world->set_defaults(); - - world->get_level()->cleanup(); + if(tux.key_event(key, UP)) + break; - if (st_gl_mode == ST_GL_LOAD_LEVEL_FILE) - { - if(world->get_level()->load(subset) != 0) - return 0; - } - else - { - if(world->get_level()->load(subset, levelnb) != 0) - return 0; - } + 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; + } + } - world->arrays_free(); - world->activate_bad_guys(); - world->activate_particle_systems(); + /* 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; - world->get_level()->free_gfx(); - world->get_level()->load_gfx(); - world->get_level()->free_song(); - world->get_level()->load_song(); + 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; - if(st_gl_mode != ST_GL_TEST) - levelintro(); - start_timers(); - /* Play music: */ - play_current_music(); + 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; + + default: + break; + } /* switch */ + } + } /* while */ } +} - tux.action(frame_ratio); +void +GameSession::check_end_conditions() +{ + 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; + } +} - world->action(frame_ratio); +void +GameSession::action(float elapsed_time) +{ + // advance timers + if (exit_status == ES_NONE && !currentsector->player->growing_timer.check()) + { + // Update Tux and the World + currentsector->action(elapsed_time); + } - return -1; + // 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 = ""; + } } void GameSession::draw() { - world->draw(); - drawstatus(); + currentsector->draw(*context); + drawstatus(*context); 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); + 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); } - fillrect(0,0,screen->w,screen->h,rand() % 50, rand() % 50, rand() % 50, 128); - blue_text->drawf("PAUSE - Press 'P' To Play", 0, 230, A_HMIDDLE, A_TOP, 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); + + char str1[60]; + char str2[124]; + sprintf(str1, _("Playing: ")); + sprintf(str2, level->name.c_str()); + + 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); } - if(show_menu) + if(Menu::current()) { - menu_process_current(); - mouse_cursor->draw(); + Menu::current()->draw(*context); + mouse_cursor->draw(*context); } - updatescreen(); + context->do_drawing(); } - -int -GameSession::run() +void +GameSession::process_menu() { - Player& tux = *world->get_tux(); - current_ = this; - - int fps_cnt; - bool done; - - global_frame_counter = 0; - game_pause = false; - - fps_timer.init(true); - frame_timer.init(true); - - last_update_time = st_get_ticks(); - fps_cnt = 0; - - /* Clear screen: */ - clearscreen(0, 0, 0); - updatescreen(); - - /* Play music: */ - play_current_music(); - - // Eat unneeded events - SDL_Event event; - while (SDL_PollEvent(&event)) {} - - draw(); - - done = false; - quit = false; - while (!done && !quit) + Menu* menu = Menu::current(); + if(menu) { - /* Calculate the movement-factor */ - double 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(!frame_timer.check()) - { - frame_timer.start(25); - ++global_frame_counter; - } - - /* Handle events: */ - tux.input.old_fire = tux.input.fire; - - process_events(); - - 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 = true; - break; - } - } - else if(current_menu == options_menu) - { - process_options_menu(); - } - else if(current_menu == save_game_menu ) - { - process_save_game_menu(); - } - else if(current_menu == load_game_menu ) - { - process_load_game_menu(); - } - } + menu->action(); - - /* Handle actions: */ - - if(!game_pause && !show_menu) + if(menu == game_menu) { - /*float z = frame_ratio; - frame_ratio = 1; - while(z >= 1) - {*/ - if (action(frame_ratio) == 0) + switch (game_menu->check()) { - /* == 0: no more lives */ - /* == -1: continues */ - return 0; + case MNID_CONTINUE: + Ticks::pause_stop(); + break; + case MNID_ABORTLEVEL: + Ticks::pause_stop(); + exit_status = ES_LEVEL_ABORT; + break; } - /* --z; - }*/ } - else + else if(menu == options_menu) { - ++pause_menu_frame; - SDL_Delay(50); + process_options_menu(); } - - 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.*/ - draw(); - - /* Time stops in pause mode */ - if(game_pause || show_menu ) + else if(menu == load_game_menu ) { - continue; + process_load_game_menu(); } + } +} - /* 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) { - SDL_Delay(10); - update_time = st_get_ticks(); - } - /*if((update_time - last_update_time) < 10) - SDL_Delay((11 - (update_time - last_update_time))/2);*/ - - /* Handle time: */ - if (time_left.check()) - { - /* are we low on time ? */ - if (time_left.get_left() < TIME_WARNING - && (get_current_music() != HURRYUP_MUSIC)) /* play the fast music */ - { - set_current_music(HURRYUP_MUSIC); - play_current_music(); - } +GameSession::ExitStatus +GameSession::run() +{ + Menu::set_current(0); + current_ = this; + + int fps_cnt = 0; - } - else if(tux.dying == DYING_NOT) - tux.kill(KILL); + // Eat unneeded events + SDL_Event event; + while(SDL_PollEvent(&event)) + {} - /* Calculate frames per second */ - if(show_fps) - { - ++fps_cnt; - fps_fps = (1000.0 / (float)fps_timer.get_gone()) * (float)fps_cnt; + draw(); - if(!fps_timer.check()) - { - fps_timer.start(1000); - fps_cnt = 0; - } - } + Uint32 lastticks = SDL_GetTicks(); + fps_ticks = SDL_GetTicks(); + + 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; + + // 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; + + process_events(); + process_menu(); + + // 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()) + { + // Update the world + check_end_conditions(); + if (end_sequence == ENDSEQUENCE_RUNNING) + action(elapsed_time/2); + else if(end_sequence == NO_ENDSEQUENCE) + action(elapsed_time); + } + else + { + ++pause_menu_frame; + SDL_Delay(50); } - halt_music(); + draw(); - world->get_level()->free_gfx(); - world->get_level()->cleanup(); - world->get_level()->free_song(); + /* Time stops in pause mode */ + if(game_pause || Menu::current()) + { + continue; + } - world->arrays_free(); + //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); + } - return quit; + /* Calculate frames per second */ + if(show_fps) + { + ++fps_cnt; + + if(SDL_GetTicks() - fps_ticks >= 500) + { + fps_fps = (float) fps_cnt / .5; + fps_cnt = 0; + fps_ticks = SDL_GetTicks(); + } + } + } + + return exit_status; } -/* Bounce a brick: */ -void bumpbrick(float x, float y) +void +GameSession::respawn(const std::string& sector, const std::string& spawnpoint) { - World::current()->add_bouncy_brick(((int)(x + 1) / 32) * 32, - (int)(y / 32) * 32); + newsector = sector; + newspawnpoint = spawnpoint; +} - play_sound(sounds[SND_BRICK], SOUND_CENTER_SPEAKER); +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"; + } } /* (Status): */ void -GameSession::drawstatus() +GameSession::drawstatus(DrawingContext& context) { - Player& tux = *world->get_tux(); 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); - sprintf(str, "%d", player_status.score); - white_text->draw("SCORE", 0, 0, 1); - gold_text->draw(str, 96, 0, 1); - - if(st_gl_mode != ST_GL_TEST) + if(st_gl_mode == ST_GL_TEST) { - sprintf(str, "%d", hs_score); - white_text->draw("HIGH", 0, 20, 1); - gold_text->draw(str, 96, 20, 1); + context.draw_text(white_text, _("Press ESC To Return"), Vector(0,20), + LEFT_ALLIGN, LAYER_FOREGROUND1); } - else + + 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) { - white_text->draw("Press ESC To Return",0,20,1); + 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); } - - if (time_left.get_left() > TIME_WARNING || (global_frame_counter % 10) < 5) + else { - sprintf(str, "%d", time_left.get_left() / 1000 ); - white_text->draw("TIME", 224, 0, 1); - gold_text->draw(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", player_status.distros); - white_text->draw("DISTROS", screen->h, 0, 1); - gold_text->draw(str, 608, 0, 1); - - white_text->draw("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); - white_text->draw("FPS", screen->h, 40, 1); - gold_text->draw(str, screen->h + 60, 40, 1); - } - - for(int i= 0; i < tux.lives; ++i) - { - tux_life->draw(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 -GameSession::drawendscreen() +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); + } + } - blue_text->drawf("GAMEOVER", 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", player_status.score); - gold_text->drawf(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", player_status.distros); - gold_text->drawf(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(); + context.do_drawing(); SDL_Event event; wait_for_event(event,2000,5000,true); } -void -GameSession::drawresultscreen(void) +std::string slotinfo(int slot) { - char str[80]; - - clearscreen(0, 0, 0); - - blue_text->drawf("Result:", 0, 200, A_HMIDDLE, A_TOP, 1); - - sprintf(str, "SCORE: %d", player_status.score); - gold_text->drawf(str, 0, 224, A_HMIDDLE, A_TOP, 1); - - sprintf(str, "DISTROS: %d", player_status.distros); - gold_text->drawf(str, 0, 256, A_HMIDDLE, A_TOP, 1); - - flipscreen(); - - SDL_Event event; - wait_for_event(event,2000,5000,true); + 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 -GameSession::savegame(int) +bool process_load_game_menu() { -#if 0 - char savefile[1024]; - FILE* fi; - unsigned int ui; - - sprintf(savefile,"%s/slot%d.save",st_save_dir,slot); + int slot = load_game_menu->check(); - fi = fopen(savefile, "wb"); - - if (fi == NULL) - { - fprintf(stderr, "Warning: I could not open the slot file "); - } - else + if(slot != -1 && load_game_menu->get_item_by_id(slot).kind == MN_ACTION) { - 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); - //FIXME:fwrite(&tux,sizeof(Player),1,fi); - //FIXME:timer_fwrite(&tux.invincible_timer,fi); - //FIXME:timer_fwrite(&tux.skidding_timer,fi); - //FIXME:timer_fwrite(&tux.safe_timer,fi); - //FIXME:timer_fwrite(&tux.frame_timer,fi); - timer_fwrite(&time_left,fi); - ui = st_get_ticks(); - fwrite(&ui,sizeof(int),1,fi); - } - fclose(fi); -#endif -} + std::stringstream stream; + stream << slot; + std::string slotfile = st_save_dir + "/slot" + stream.str() + ".stsg"; -void -GameSession::loadgame(int) -{ -#if 0 - char savefile[1024]; - char str[100]; - FILE* fi; - unsigned int ui; + fadeout(256); + DrawingContext context; + context.draw_text(white_text, "Loading...", + Vector(screen->w/2, screen->h/2), CENTER_ALLIGN, LAYER_FOREGROUND1); + context.do_drawing(); - sprintf(savefile,"%s/slot%d.save",st_save_dir,slot); + WorldMapNS::WorldMap worldmap; - fi = fopen(savefile, "rb"); + worldmap.set_map_filename("/levels/world1/worldmap.stwm"); + // Load the game or at least set the savegame_file variable + worldmap.loadgame(slotfile); - if (fi == NULL) - { - fprintf(stderr, "Warning: I could not open the slot file "); + worldmap.display(); - } - else - { - fgets(str, 100, fi); - strcpy(level_subset, str); - level_subset[strlen(level_subset)-1] = '\0'; - fread(&level,sizeof(int),1,fi); - - world->set_defaults(); - world->get_level()->cleanup(); - world->arrays_free(); - world->get_level()->free_gfx(); - world->get_level()->free_song(); - - if(world->get_level()->load(level_subset,level) != 0) - exit(1); - - world->activate_bad_guys(); - world->activate_particle_systems(); - world->get_level()->load_gfx(); - world->get_level()->load_song(); - - 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); - //FIXME:fread(&tux, sizeof(Player), 1, fi); - //FIXME:timer_fread(&tux.invincible_timer,fi); - //FIXME:timer_fread(&tux.skidding_timer,fi); - //FIXME:timer_fread(&tux.safe_timer,fi); - //FIXME:timer_fread(&tux.frame_timer,fi); - timer_fread(&time_left,fi); - fread(&ui,sizeof(int),1,fi); - fclose(fi); - } -#endif -} + Menu::set_current(main_menu); -std::string slotinfo(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); - - fi = fopen(slotfile, "rb"); - - sprintf(tmp,"Slot %d - ",slot); - - 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; } - - return tmp; } -