00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017 #include "supertux/game_session.hpp"
00018
00019 #include <float.h>
00020 #include <fstream>
00021
00022 #include "audio/sound_manager.hpp"
00023 #include "control/joystickkeyboardcontroller.hpp"
00024 #include "gui/menu.hpp"
00025 #include "gui/menu_manager.hpp"
00026 #include "math/random_generator.hpp"
00027 #include "object/camera.hpp"
00028 #include "object/endsequence_fireworks.hpp"
00029 #include "object/endsequence_walkleft.hpp"
00030 #include "object/endsequence_walkright.hpp"
00031 #include "object/level_time.hpp"
00032 #include "object/player.hpp"
00033 #include "scripting/squirrel_util.hpp"
00034 #include "supertux/gameconfig.hpp"
00035 #include "supertux/levelintro.hpp"
00036 #include "supertux/globals.hpp"
00037 #include "supertux/screen_manager.hpp"
00038 #include "supertux/menu/menu_storage.hpp"
00039 #include "supertux/menu/game_menu.hpp"
00040 #include "supertux/menu/options_menu.hpp"
00041 #include "supertux/sector.hpp"
00042 #include "util/file_system.hpp"
00043 #include "util/gettext.hpp"
00044 #include "worldmap/worldmap.hpp"
00045
00046 GameSession::GameSession(const std::string& levelfile_, PlayerStatus* player_status, Statistics* statistics) :
00047 level(0),
00048 statistics_backdrop(),
00049 scripts(),
00050 currentsector(0),
00051 levelnb(),
00052 pause_menu_frame(),
00053 end_sequence(0),
00054 game_pause(),
00055 speed_before_pause(),
00056 levelfile(levelfile_),
00057 reset_sector(),
00058 reset_pos(),
00059 newsector(),
00060 newspawnpoint(),
00061 best_level_statistics(statistics),
00062 player_status(player_status),
00063 capture_demo_stream(0),
00064 capture_file(),
00065 playback_demo_stream(0),
00066 demo_controller(0),
00067 game_menu(),
00068 play_time(0),
00069 edit_mode(false),
00070 levelintro_shown(false)
00071 {
00072 currentsector = NULL;
00073
00074 game_pause = false;
00075 speed_before_pause = g_screen_manager->get_speed();
00076
00077 statistics_backdrop = Surface::create("images/engine/menu/score-backdrop.png");
00078
00079 if (restart_level() != 0)
00080 throw std::runtime_error ("Initializing the level failed.");
00081
00082 game_menu.reset(new GameMenu(*level));
00083 }
00084
00085 int
00086 GameSession::restart_level()
00087 {
00088
00089 if (edit_mode) {
00090 force_ghost_mode();
00091 return (-1);
00092 }
00093
00094 game_pause = false;
00095 end_sequence = 0;
00096
00097 g_jk_controller->reset();
00098
00099 currentsector = 0;
00100
00101 level.reset(new Level);
00102 try {
00103 level->load(levelfile);
00104 level->stats.total_coins = level->get_total_coins();
00105 level->stats.total_badguys = level->get_total_badguys();
00106 level->stats.total_secrets = level->get_total_secrets();
00107 level->stats.reset();
00108
00109 if(reset_sector != "") {
00110 currentsector = level->get_sector(reset_sector);
00111 if(!currentsector) {
00112 std::stringstream msg;
00113 msg << "Couldn't find sector '" << reset_sector << "' for resetting tux.";
00114 throw std::runtime_error(msg.str());
00115 }
00116 level->stats.declare_invalid();
00117 currentsector->activate(reset_pos);
00118 } else {
00119 currentsector = level->get_sector("main");
00120 if(!currentsector)
00121 throw std::runtime_error("Couldn't find main sector");
00122 play_time = 0;
00123 currentsector->activate("main");
00124 }
00125 } catch(std::exception& e) {
00126 log_fatal << "Couldn't start level: " << e.what() << std::endl;
00127 g_screen_manager->exit_screen();
00128 return (-1);
00129 }
00130
00131 sound_manager->stop_music();
00132 currentsector->play_music(LEVEL_MUSIC);
00133
00134 if(capture_file != "") {
00135 int newSeed=0;
00136 while (newSeed == 0)
00137 newSeed = gameRandom.rand();
00138 g_config->random_seed = gameRandom.srand(newSeed);
00139 log_info << "Next run uses random seed " << g_config->random_seed <<std::endl;
00140 record_demo(capture_file);
00141 }
00142
00143 return (0);
00144 }
00145
00146 GameSession::~GameSession()
00147 {
00148 delete capture_demo_stream;
00149 delete playback_demo_stream;
00150 delete demo_controller;
00151 }
00152
00153 void
00154 GameSession::record_demo(const std::string& filename)
00155 {
00156 delete capture_demo_stream;
00157
00158 capture_demo_stream = new std::ofstream(filename.c_str());
00159 if(!capture_demo_stream->good()) {
00160 std::stringstream msg;
00161 msg << "Couldn't open demo file '" << filename << "' for writing.";
00162 throw std::runtime_error(msg.str());
00163 }
00164 capture_file = filename;
00165
00166 char buf[30];
00167 snprintf(buf, sizeof(buf), "random_seed=%10d", g_config->random_seed);
00168 for (int i=0; i==0 || buf[i-1]; i++)
00169 capture_demo_stream->put(buf[i]);
00170 }
00171
00172 int
00173 GameSession::get_demo_random_seed(const std::string& filename)
00174 {
00175 std::istream* test_stream = new std::ifstream(filename.c_str());
00176 if(test_stream->good()) {
00177 char buf[30];
00178 int seed;
00179 for (int i=0; i<30 && (i==0 || buf[i-1]); i++)
00180 test_stream->get(buf[i]);
00181 if (sscanf(buf, "random_seed=%10d", &seed) == 1) {
00182 log_info << "Random seed " << seed << " from demo file" << std::endl;
00183 return seed;
00184 }
00185 else
00186 log_info << "Demo file contains no random number" << std::endl;
00187 }
00188 return 0;
00189 }
00190
00191 void
00192 GameSession::play_demo(const std::string& filename)
00193 {
00194 delete playback_demo_stream;
00195 delete demo_controller;
00196
00197 playback_demo_stream = new std::ifstream(filename.c_str());
00198 if(!playback_demo_stream->good()) {
00199 std::stringstream msg;
00200 msg << "Couldn't open demo file '" << filename << "' for reading.";
00201 throw std::runtime_error(msg.str());
00202 }
00203
00204 Player& tux = *currentsector->player;
00205 demo_controller = new CodeController();
00206 tux.set_controller(demo_controller);
00207
00208
00209 char buf[30];
00210 int seed;
00211 for (int i=0; i<30 && (i==0 || buf[i-1]); i++)
00212 playback_demo_stream->get(buf[i]);
00213 if (sscanf(buf, "random_seed=%010d", &seed) != 1)
00214 playback_demo_stream->seekg(0);
00215 }
00216
00217 void
00218 GameSession::on_escape_press()
00219 {
00220 if(currentsector->player->is_dying() || end_sequence)
00221 {
00222
00223 if (end_sequence)
00224 end_sequence->stop();
00225
00226 currentsector->player->dying_timer.start(FLT_EPSILON);
00227 return;
00228 }
00229
00230 if(level->on_menukey_script != "") {
00231 std::istringstream in(level->on_menukey_script);
00232 run_script(in, "OnMenuKeyScript");
00233 } else {
00234 toggle_pause();
00235 }
00236 }
00237
00238 void
00239 GameSession::toggle_pause()
00240 {
00241
00242 if(!game_pause) {
00243 speed_before_pause = g_screen_manager->get_speed();
00244 g_screen_manager->set_speed(0);
00245 MenuManager::set_current(game_menu.get());
00246 game_menu->set_active_item(MNID_CONTINUE);
00247 game_pause = true;
00248 }
00249
00250
00251 }
00252
00253 void
00254 GameSession::set_editmode(bool edit_mode)
00255 {
00256 if (this->edit_mode == edit_mode) return;
00257 this->edit_mode = edit_mode;
00258
00259 currentsector->get_players()[0]->set_edit_mode(edit_mode);
00260
00261 if (edit_mode) {
00262
00263
00264
00265 } else {
00266
00267
00268 restart_level();
00269
00270 }
00271 }
00272
00273 void
00274 GameSession::force_ghost_mode()
00275 {
00276 currentsector->get_players()[0]->set_ghost_mode(true);
00277 }
00278
00279 HSQUIRRELVM
00280 GameSession::run_script(std::istream& in, const std::string& sourcename)
00281 {
00282 using namespace scripting;
00283
00284
00285 for(ScriptList::iterator i = scripts.begin();
00286 i != scripts.end(); ) {
00287 HSQOBJECT& object = *i;
00288 HSQUIRRELVM vm = object_to_vm(object);
00289
00290 if(sq_getvmstate(vm) != SQ_VMSTATE_SUSPENDED) {
00291 sq_release(global_vm, &object);
00292 i = scripts.erase(i);
00293 continue;
00294 }
00295
00296 ++i;
00297 }
00298
00299 HSQOBJECT object = create_thread(global_vm);
00300 scripts.push_back(object);
00301
00302 HSQUIRRELVM vm = object_to_vm(object);
00303
00304 compile_and_run(vm, in, sourcename);
00305
00306 return vm;
00307 }
00308
00309 void
00310 GameSession::process_events()
00311 {
00312
00313
00314
00315
00316
00317
00318
00319
00320
00321
00322
00323 if(playback_demo_stream != 0) {
00324 demo_controller->update();
00325 char left = false;
00326 char right = false;
00327 char up = false;
00328 char down = false;
00329 char jump = false;
00330 char action = false;
00331 playback_demo_stream->get(left);
00332 playback_demo_stream->get(right);
00333 playback_demo_stream->get(up);
00334 playback_demo_stream->get(down);
00335 playback_demo_stream->get(jump);
00336 playback_demo_stream->get(action);
00337 demo_controller->press(Controller::LEFT, left);
00338 demo_controller->press(Controller::RIGHT, right);
00339 demo_controller->press(Controller::UP, up);
00340 demo_controller->press(Controller::DOWN, down);
00341 demo_controller->press(Controller::JUMP, jump);
00342 demo_controller->press(Controller::ACTION, action);
00343 }
00344
00345
00346 if(capture_demo_stream != 0) {
00347 Controller *controller = g_jk_controller->get_main_controller();
00348 capture_demo_stream ->put(controller->hold(Controller::LEFT));
00349 capture_demo_stream ->put(controller->hold(Controller::RIGHT));
00350 capture_demo_stream ->put(controller->hold(Controller::UP));
00351 capture_demo_stream ->put(controller->hold(Controller::DOWN));
00352 capture_demo_stream ->put(controller->hold(Controller::JUMP));
00353 capture_demo_stream ->put(controller->hold(Controller::ACTION));
00354 }
00355 }
00356
00357 void
00358 GameSession::check_end_conditions()
00359 {
00360 Player* tux = currentsector->player;
00361
00362
00363 if(end_sequence && end_sequence->is_done()) {
00364 finish(true);
00365 } else if (!end_sequence && tux->is_dead()) {
00366 restart_level();
00367 }
00368 }
00369
00370 void
00371 GameSession::draw(DrawingContext& context)
00372 {
00373 currentsector->draw(context);
00374 drawstatus(context);
00375
00376 if(game_pause)
00377 draw_pause(context);
00378 }
00379
00380 void
00381 GameSession::draw_pause(DrawingContext& context)
00382 {
00383 context.draw_filled_rect(
00384 Vector(0,0), Vector(SCREEN_WIDTH, SCREEN_HEIGHT),
00385 Color(0.0f, 0.0f, 0.0f, .25f), LAYER_FOREGROUND1);
00386 }
00387
00388 void
00389 GameSession::process_menu()
00390 {
00391 Menu* menu = MenuManager::current();
00392 if(menu) {
00393 if(menu == game_menu.get()) {
00394 switch (game_menu->check()) {
00395 case MNID_CONTINUE:
00396 MenuManager::set_current(0);
00397 toggle_pause();
00398 break;
00399 case MNID_ABORTLEVEL:
00400 MenuManager::set_current(0);
00401 g_screen_manager->exit_screen();
00402 break;
00403 }
00404 }
00405 }
00406 }
00407
00408 void
00409 GameSession::setup()
00410 {
00411 if (currentsector == NULL)
00412 return;
00413
00414 if(currentsector != Sector::current()) {
00415 currentsector->activate(currentsector->player->get_pos());
00416 }
00417 currentsector->play_music(LEVEL_MUSIC);
00418
00419
00420 SDL_Event event;
00421 while(SDL_PollEvent(&event))
00422 {}
00423
00424 if (!levelintro_shown) {
00425 levelintro_shown = true;
00426 g_screen_manager->push_screen(new LevelIntro(level.get(), best_level_statistics));
00427 }
00428 }
00429
00430 void
00431 GameSession::update(float elapsed_time)
00432 {
00433
00434 if(g_jk_controller->get_main_controller()->pressed(Controller::PAUSE_MENU))
00435 on_escape_press();
00436
00437 process_events();
00438 process_menu();
00439
00440
00441 if (game_pause && !MenuManager::current()) {
00442 g_screen_manager->set_speed(speed_before_pause);
00443 game_pause = false;
00444 }
00445
00446 check_end_conditions();
00447
00448
00449 if(newsector != "" && newspawnpoint != "") {
00450 Sector* sector = level->get_sector(newsector);
00451 if(sector == 0) {
00452 log_warning << "Sector '" << newsector << "' not found" << std::endl;
00453 sector = level->get_sector("main");
00454 }
00455 sector->activate(newspawnpoint);
00456 sector->play_music(LEVEL_MUSIC);
00457 currentsector = sector;
00458
00459 if(edit_mode)
00460 currentsector->get_players()[0]->set_edit_mode(edit_mode);
00461 newsector = "";
00462 newspawnpoint = "";
00463 }
00464
00465
00466 if(!game_pause) {
00467
00468 if (!end_sequence) {
00469 play_time += elapsed_time;
00470 level->stats.time = play_time;
00471 currentsector->update(elapsed_time);
00472 } else {
00473 if (!end_sequence->is_tux_stopped()) {
00474 currentsector->update(elapsed_time);
00475 } else {
00476 end_sequence->update(elapsed_time);
00477 }
00478 }
00479 }
00480
00481
00482 if (currentsector && currentsector->camera) sound_manager->set_listener_position(currentsector->camera->get_center());
00483
00484
00485 if (end_sequence)
00486 return;
00487
00488 if(currentsector->player->invincible_timer.started()) {
00489 if(currentsector->player->invincible_timer.get_timeleft() <=
00490 TUX_INVINCIBLE_TIME_WARNING) {
00491 currentsector->play_music(HERRING_WARNING_MUSIC);
00492 } else {
00493 currentsector->play_music(HERRING_MUSIC);
00494 }
00495 } else if(currentsector->get_music_type() != LEVEL_MUSIC) {
00496 currentsector->play_music(LEVEL_MUSIC);
00497 }
00498 }
00499
00500 void
00501 GameSession::finish(bool win)
00502 {
00503 using namespace worldmap;
00504
00505 if (edit_mode) {
00506 force_ghost_mode();
00507 return;
00508 }
00509
00510 if(win) {
00511 if(WorldMap::current())
00512 WorldMap::current()->finished_level(level.get());
00513 }
00514
00515 g_screen_manager->exit_screen();
00516 }
00517
00518 void
00519 GameSession::respawn(const std::string& sector, const std::string& spawnpoint)
00520 {
00521 newsector = sector;
00522 newspawnpoint = spawnpoint;
00523 }
00524
00525 void
00526 GameSession::set_reset_point(const std::string& sector, const Vector& pos)
00527 {
00528 reset_sector = sector;
00529 reset_pos = pos;
00530 }
00531
00532 std::string
00533 GameSession::get_working_directory()
00534 {
00535 return FileSystem::dirname(levelfile);
00536 }
00537
00538 void
00539 GameSession::start_sequence(const std::string& sequencename)
00540 {
00541
00542 if (edit_mode) {
00543 force_ghost_mode();
00544 return;
00545 }
00546
00547
00548 if (sequencename == "stoptux") {
00549 if (!end_sequence) {
00550 log_warning << "Final target reached without an active end sequence" << std::endl;
00551 this->start_sequence("endsequence");
00552 }
00553 if (end_sequence) end_sequence->stop_tux();
00554 return;
00555 }
00556
00557
00558 if (end_sequence)
00559 return;
00560
00561 if (sequencename == "endsequence") {
00562 if (currentsector->get_players()[0]->get_physic().get_velocity_x() < 0) {
00563 end_sequence = new EndSequenceWalkLeft();
00564 } else {
00565 end_sequence = new EndSequenceWalkRight();
00566 }
00567 } else if (sequencename == "fireworks") {
00568 end_sequence = new EndSequenceFireworks();
00569 } else {
00570 log_warning << "Unknown sequence '" << sequencename << "'. Ignoring." << std::endl;
00571 return;
00572 }
00573
00574
00575 g_screen_manager->set_speed(0.5f);
00576
00577 currentsector->add_object(end_sequence);
00578 end_sequence->start();
00579
00580 sound_manager->play_music("music/leveldone.ogg", false);
00581 currentsector->player->set_winning();
00582
00583
00584 for(std::vector<GameObject*>::iterator i = currentsector->gameobjects.begin();
00585 i != currentsector->gameobjects.end(); ++i)
00586 {
00587 GameObject* obj = *i;
00588
00589 LevelTime* lt = dynamic_cast<LevelTime*> (obj);
00590 if(lt)
00591 lt->stop();
00592 }
00593 }
00594
00595
00596 void
00597 GameSession::drawstatus(DrawingContext& context)
00598 {
00599 player_status->draw(context);
00600
00601
00602 if (end_sequence) {
00603 level->stats.draw_endseq_panel(context, best_level_statistics, statistics_backdrop);
00604 }
00605 }
00606
00607