playcampaign.cpp

Go to the documentation of this file.
00001 /* $Id: playcampaign.cpp 26501 2008-05-09 22:44:58Z mordante $ */
00002 /*
00003    Copyright (C) 2003-2005 by David White <dave@whitevine.net>
00004    Copyright (C) 2005 - 2008 by Philippe Plantier <ayin@anathas.org>
00005    Part of the Battle for Wesnoth Project http://www.wesnoth.org
00006 
00007    This program is free software; you can redistribute it and/or modify
00008    it under the terms of the GNU General Public License version 2
00009    or at your option any later version.
00010    This program is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY.
00012 
00013    See the COPYING file for more details.
00014 */
00015 
00016 //! @file playcampaign.cpp
00017 //! Controls setup, play, (auto)save and replay of campaigns.
00018 
00019 #include "global.hpp"
00020 
00021 #include "playcampaign.hpp"
00022 #include "config.hpp"
00023 #include "filesystem.hpp"
00024 #include "game_preferences.hpp"
00025 #include "gamestatus.hpp"
00026 #include "map_create.hpp"
00027 #include "playmp_controller.hpp"
00028 #include "playsingle_controller.hpp"
00029 #include "replay.hpp"
00030 #include "replay_controller.hpp"
00031 #include "log.hpp"
00032 #include "dialogs.hpp"
00033 #include "gettext.hpp"
00034 #include "game_errors.hpp"
00035 #include "sound.hpp"
00036 #include "wml_exception.hpp"
00037 
00038 #include <cassert>
00039 #include <map>
00040 
00041 #define LOG_G LOG_STREAM(info, general)
00042 #define LOG_NG LOG_STREAM(info, engine)
00043 
00044 namespace {
00045 
00046 struct player_controller
00047 {
00048     player_controller() :
00049         controller(),
00050         description()
00051         {}
00052 
00053     player_controller(const std::string& controller, const std::string& description) :
00054         controller(controller),
00055         description(description)
00056         {}
00057 
00058     std::string controller;
00059     std::string description;
00060 };
00061 
00062 typedef std::map<std::string, player_controller> controller_map;
00063 
00064 } // end anon namespace
00065 
00066 void play_replay(display& disp, game_state& gamestate, const config& game_config,
00067         CVideo& video)
00068 {
00069     std::string type = gamestate.campaign_type;
00070     if(type.empty())
00071         type = "scenario";
00072 
00073     config const* scenario = NULL;
00074 
00075     // 'starting_pos' will contain the position we start the game from.
00076     config starting_pos;
00077 
00078     if (gamestate.starting_pos.empty()){
00079         // Backwards compatibility code for 1.2 and 1.2.1
00080         scenario = game_config.find_child(type,"id",gamestate.scenario);
00081         gamestate.starting_pos = *scenario;
00082     }
00083     recorder.set_save_info(gamestate);
00084     starting_pos = gamestate.starting_pos;
00085     scenario = &starting_pos;
00086 
00087     try {
00088         // Preserve old label eg. replay
00089         if (gamestate.label.empty())
00090             gamestate.label = (*scenario)["name"];
00091         //if (gamestate.abbrev.empty())
00092         //  gamestate.abbrev = (*scenario)["abbrev"];
00093 
00094         play_replay_level(game_config,scenario,video,gamestate);
00095 
00096         gamestate.snapshot = config();
00097         recorder.clear();
00098         gamestate.replay_data.clear();
00099 
00100     } catch(game::load_game_failed& e) {
00101         gui::show_error_message(disp, _("The game could not be loaded: ") + e.message);
00102     } catch(game::game_error& e) {
00103         gui::show_error_message(disp, _("Error while playing the game: ") + e.message);
00104     } catch(gamemap::incorrect_format_exception& e) {
00105         gui::show_error_message(disp, std::string(_("The game map could not be loaded: ")) + e.msg_);
00106     } catch(twml_exception& e) {
00107         e.show(disp);
00108     }
00109 }
00110 
00111 static void clean_saves(const std::string &label)
00112 {
00113     std::vector<save_info> games = get_saves_list();
00114     std::string prefix = label + "-" + _("Auto-Save");
00115     std::cerr << "Cleaning saves with prefix '" << prefix << "'\n";
00116     for (std::vector<save_info>::iterator i = games.begin(); i != games.end(); i++) {
00117         if (i->name.compare(0, prefix.length(), prefix) == 0) {
00118             std::cerr << "Deleting savegame '" << i->name << "'\n";
00119             delete_game(i->name);
00120         }
00121     }
00122 }
00123 
00124 static LEVEL_RESULT playsingle_scenario(const config& game_config,
00125         const config* level, display& disp, game_state& state_of_game,
00126         const std::vector<config*>& story, upload_log& log, bool skip_replay)
00127 {
00128     const int ticks = SDL_GetTicks();
00129     const int num_turns = atoi((*level)["turns"].c_str());
00130     LOG_NG << "creating objects... " << (SDL_GetTicks() - ticks) << "\n";
00131     playsingle_controller playcontroller(*level, state_of_game, ticks, num_turns, game_config, disp.video(), skip_replay);
00132     LOG_NG << "created objects... " << (SDL_GetTicks() - playcontroller.get_ticks()) << "\n";
00133 
00134     const LEVEL_RESULT res = playcontroller.play_scenario(story, log, skip_replay);
00135 
00136     if (res == DEFEAT) {
00137         gui::message_dialog(disp,
00138                     _("Defeat"),
00139                     _("You have been defeated!")
00140                     ).show();
00141     }
00142 
00143     if (!disp.video().faked() && res != QUIT && res != LEVEL_CONTINUE && res != LEVEL_CONTINUE_NO_SAVE)
00144         try {
00145             playcontroller.linger(log);
00146         } catch(end_level_exception& e) {
00147             if (e.result == QUIT) {
00148                 return QUIT;
00149             }
00150         }
00151 
00152     return res;
00153 }
00154 
00155 
00156 static LEVEL_RESULT playmp_scenario(const config& game_config,
00157         config const* level, display& disp, game_state& state_of_game,
00158         const config::child_list& story, upload_log& log, bool skip_replay,
00159         io_type_t& io_type)
00160 {
00161     const int ticks = SDL_GetTicks();
00162     const int num_turns = atoi((*level)["turns"].c_str());
00163     playmp_controller playcontroller(*level, state_of_game, ticks, num_turns,
00164         game_config, disp.video(), skip_replay, io_type == IO_SERVER);
00165     const LEVEL_RESULT res = playcontroller.play_scenario(story, log, skip_replay);
00166 
00167     //Check if the player started as mp client and changed to host
00168     if (io_type == IO_CLIENT && playcontroller.is_host())
00169         io_type = IO_SERVER;
00170 
00171     if (res == DEFEAT) {
00172         gui::message_dialog(disp,
00173                     _("Defeat"),
00174                     _("You have been defeated!")
00175                     ).show();
00176     }
00177 
00178     if (!disp.video().faked() && res != QUIT) {
00179         if(res == LEVEL_CONTINUE || res == LEVEL_CONTINUE_NO_SAVE) {
00180             if(!playcontroller.is_host()) {
00181                 // If we continue without lingering we need to
00182                 // make sure the host uploads the next scenario
00183                 // before we attempt to download it.
00184                 playcontroller.wait_for_upload();
00185             }
00186         } else {
00187             try {
00188                 playcontroller.linger(log);
00189             } catch(end_level_exception& e) {
00190                 if (e.result == QUIT) {
00191                     return QUIT;
00192                 }
00193             }
00194         }
00195     }
00196 
00197     return res;
00198 }
00199 
00200 LEVEL_RESULT play_game(display& disp, game_state& gamestate, const config& game_config,
00201         upload_log &log, io_type_t io_type, bool skip_replay)
00202 {
00203     std::string type = gamestate.campaign_type;
00204     if(type.empty())
00205         type = "scenario";
00206 
00207     config const* scenario = NULL;
00208 
00209     // 'starting_pos' will contain the position we start the game from.
00210     config starting_pos;
00211 
00212     recorder.set_save_info(gamestate);
00213 
00214     // Do we have any snapshot data?
00215     // yes => this must be a savegame
00216     // no  => we are starting a fresh scenario
00217     if(gamestate.snapshot.child("side") == NULL || !recorder.at_end()) {
00218         gamestate.completion = "running";
00219         recorder.set_save_info_completion(gamestate.completion);
00220         // Campaign or Multiplayer?
00221         // If the gamestate already contains a starting_pos,
00222         // then we are starting a fresh multiplayer game.
00223         // Otherwise this is the start of a campaign scenario.
00224         if(gamestate.starting_pos.empty() == false) {
00225             LOG_G << "loading starting position...\n";
00226             starting_pos = gamestate.starting_pos;
00227             scenario = &starting_pos;
00228         } else {
00229             LOG_G << "loading scenario: '" << gamestate.scenario << "'\n";
00230             scenario = game_config.find_child(type,"id",gamestate.scenario);
00231             if(scenario) {
00232                 starting_pos = *scenario;
00233                 gamestate.starting_pos = *scenario;
00234             }
00235             LOG_G << "scenario found: " << (scenario != NULL ? "yes" : "no") << "\n";
00236         }
00237     } else {
00238         // This game was started from a savegame
00239         LOG_G << "loading snapshot...\n";
00240         starting_pos = gamestate.starting_pos;
00241         scenario = &gamestate.snapshot;
00242         // When starting wesnoth --multiplayer there might be
00243         // no variables which leads to a segfault
00244         if(gamestate.snapshot.child("variables") != NULL) {
00245             gamestate.set_variables(*gamestate.snapshot.child("variables"));
00246         }
00247         gamestate.set_menu_items(gamestate.snapshot.get_children("menu_item"));
00248         // Replace game label with that from snapshot
00249         if (!gamestate.snapshot["label"].empty()){
00250             gamestate.label = gamestate.snapshot["label"];
00251         }
00252         {
00253             // Get the current gold values of players, so they don't start
00254             // with the amount they had at the start of the scenario
00255             const std::vector<config*>& player_cfg = gamestate.snapshot.get_children("player");
00256             for (std::vector<config*>::const_iterator p = player_cfg.begin(); p != player_cfg.end(); p++){
00257                 std::string save_id = (**p)["save_id"];
00258                 player_info* player = gamestate.get_player(save_id);
00259                 if (player != NULL){
00260                     player->gold = lexical_cast <int> ((**p)["gold"]);
00261                 }
00262             }
00263         }
00264         {
00265             // Also get the recruitment list if there are some specialties in this scenario
00266             const std::vector<config*>& player_cfg = gamestate.snapshot.get_children("side");
00267             for (std::vector<config*>::const_iterator p = player_cfg.begin(); p != player_cfg.end(); p++){
00268                 std::string save_id = (**p)["save_id"];
00269                 player_info* player = gamestate.get_player(save_id);
00270                 if (player != NULL){
00271                     const std::string& can_recruit_str = (**p)["recruit"];
00272                     if(can_recruit_str != "") {
00273                         player->can_recruit.clear();
00274                         const std::vector<std::string> can_recruit = utils::split(can_recruit_str);
00275                         std::copy(can_recruit.begin(),can_recruit.end(),std::inserter(player->can_recruit,player->can_recruit.end()));
00276                     }
00277                 }
00278             }
00279         }
00280     }
00281 
00282     controller_map controllers;
00283 
00284     if(io_type == IO_SERVER) {
00285         const config::child_list& sides_list = scenario->get_children("side");
00286         for(config::child_list::const_iterator side = sides_list.begin();
00287                 side != sides_list.end(); ++side) {
00288             if ((**side)["current_player"] == preferences::login())
00289             {
00290                 (**side)["controller"] = preferences::client_type();
00291             }
00292             std::string id = (**side)["save_id"];
00293             if(id.empty())
00294                 continue;
00295             controllers[id] = player_controller((**side)["controller"],
00296                     (**side)["id"]);
00297         }
00298     }
00299 
00300     while(scenario != NULL) {
00301         // If we are a multiplayer client, tweak the controllers
00302         if(io_type == IO_CLIENT) {
00303             if(scenario != &starting_pos) {
00304                 starting_pos = *scenario;
00305                 scenario = &starting_pos;
00306             }
00307 
00308             const config::child_list& sides_list = starting_pos.get_children("side");
00309             for(config::child_list::const_iterator side = sides_list.begin();
00310                     side != sides_list.end(); ++side) {
00311                 if((**side)["current_player"] == preferences::login()) {
00312                     (**side)["controller"] = preferences::client_type();
00313                     (**side)["persistent"] = "1";
00314                 } else if((**side)["controller"] != "null") {
00315                     (**side)["controller"] = "network";
00316                     (**side)["persistent"] = "0";
00317                 }
00318             }
00319         }
00320 
00321         const config::child_list& story = scenario->get_children("story");
00322         gamestate.next_scenario = (*scenario)["next_scenario"];
00323 
00324         bool save_game_after_scenario = true;
00325 
00326         const set_random_generator generator_setter(&recorder);
00327         LEVEL_RESULT res = LEVEL_CONTINUE;
00328 
00329         try {
00330             // Preserve old label eg. replay
00331             if (gamestate.label.empty()) {
00332                 if (gamestate.abbrev.empty())
00333                     gamestate.label = (*scenario)["name"];
00334                 else {
00335                     gamestate.label = std::string(gamestate.abbrev);
00336                     gamestate.label.append("-");
00337                     gamestate.label.append((*scenario)["name"]);
00338                 }
00339             }
00340 
00341             // If the entire scenario should be randomly generated
00342             if((*scenario)["scenario_generation"] != "") {
00343                 LOG_G << "randomly generating scenario...\n";
00344                 const cursor::setter cursor_setter(cursor::WAIT);
00345 
00346                 static config scenario2;
00347                 scenario2 = random_generate_scenario((*scenario)["scenario_generation"], scenario->child("generator"));
00348                 //level_ = scenario;
00349 
00350                 gamestate.starting_pos = scenario2;
00351                 scenario = &scenario2;
00352             }
00353             std::string map_data = (*scenario)["map_data"];
00354             if(map_data.empty() && (*scenario)["map"] != "") {
00355                 map_data = read_map((*scenario)["map"]);
00356             }
00357 
00358             // If the map should be randomly generated
00359             if(map_data.empty() && (*scenario)["map_generation"] != "") {
00360                 const cursor::setter cursor_setter(cursor::WAIT);
00361                 map_data = random_generate_map((*scenario)["map_generation"],scenario->child("generator"));
00362 
00363                 // Since we've had to generate the map,
00364                 // make sure that when we save the game,
00365                 // it will not ask for the map to be generated again on reload
00366                 static config new_level;
00367                 new_level = *scenario;
00368                 new_level.values["map_data"] = map_data;
00369                 scenario = &new_level;
00370 
00371                 gamestate.starting_pos = new_level;
00372                 LOG_G << "generated map\n";
00373             }
00374 
00375             sound::play_no_music();
00376 
00377             switch (io_type){
00378             case IO_NONE:
00379                 res = playsingle_scenario(game_config,scenario,disp,gamestate,story,log, skip_replay);
00380                 break;
00381             case IO_SERVER:
00382             case IO_CLIENT:
00383                 res = playmp_scenario(game_config,scenario,disp,gamestate,story,log, skip_replay, io_type);
00384                 break;
00385             }
00386         } catch(game::load_game_failed& e) {
00387             gui::show_error_message(disp, _("The game could not be loaded: ") + e.message);
00388             return QUIT;
00389         } catch(game::game_error& e) {
00390             gui::show_error_message(disp, _("Error while playing the game: ") + e.message);
00391             return QUIT;
00392         } catch(gamemap::incorrect_format_exception& e) {
00393             gui::show_error_message(disp, std::string(_("The game map could not be loaded: ")) + e.msg_);
00394             return QUIT;
00395         } catch(config::error& e) {
00396             std::cerr << "caught config::error...\n";
00397             gui::show_error_message(disp, _("Error while reading the WML: ") + e.message);
00398             return QUIT;
00399         } catch(twml_exception& e) {
00400             e.show(disp);
00401             return QUIT;
00402         }
00403 
00404         gamestate.snapshot = config();
00405 
00406         // Save-nagement ioptions fire on game end.
00407         // This means: (a) we have a victory, or
00408         // or (b) we're multiplayer live, in which
00409         // case defeat is also game end.  Someday,
00410         // if MP campaigns ever work again, we might
00411         // need to change this test.
00412 
00413         // OBSERVER_END probably can be removed here if the observer disconnect
00414         // bug (#10077) is fixed so that when the host ends the game observers
00415         // again get asked if they want to save a replay of the game
00416         if (res == VICTORY || (io_type != IO_NONE && (res == DEFEAT || res == OBSERVER_END))) {
00417             if (preferences::delete_saves())
00418                 clean_saves(gamestate.label);
00419 
00420             if (preferences::save_replays()) {
00421                 std::string label = gamestate.label + _(" replay");
00422                 if (dialogs::get_save_name(disp, "", _("Name: "), &label,
00423                     gui::OK_CANCEL, _("Save Replay"), false, false) == 0) {
00424                 try {
00425                         config snapshot;
00426                         recorder.save_game(label, snapshot, gamestate.starting_pos);
00427                     } catch(game::save_game_failed&) {
00428                         gui::show_error_message(disp, _("The replay could not be saved"));
00429                     }
00430                 }
00431             }
00432         }
00433 
00434         recorder.clear();
00435         gamestate.replay_data.clear();
00436 
00437         // On DEFEAT, QUIT, or OBSERVER_END, we're done now
00438         if (res != VICTORY && res != LEVEL_CONTINUE_NO_SAVE
00439             && res != LEVEL_CONTINUE)
00440         {
00441             if (res != OBSERVER_END || gamestate.next_scenario.empty())
00442                 return res;
00443 
00444             const int dlg_res = gui::dialog(disp,"Game Over",
00445                 _("This scenario has ended. Do you want to continue the campaign?"),
00446                 gui::YES_NO).show();
00447             if (dlg_res != 0)
00448                 return res;
00449         }
00450 
00451         // Continue without saving is like a victory,
00452         // but the save game dialog isn't displayed
00453         if(res == LEVEL_CONTINUE_NO_SAVE)
00454             save_game_after_scenario = false;
00455 
00456         // Switch to the next scenario.
00457         gamestate.scenario = gamestate.next_scenario;
00458         gamestate.rng().rotate_random();
00459 
00460         if(io_type == IO_CLIENT) {
00461             if (gamestate.next_scenario.empty()) return res;
00462 
00463             // Ask for the next scenario data.
00464             network::send_data(config("load_next_scenario"), 0, true);
00465             config cfg;
00466             std::string msg = _("Downloading next scenario...");
00467             do {
00468                 cfg.clear();
00469                 network::connection data_res = dialogs::network_receive_dialog(disp,
00470                         msg, cfg);
00471                 if(!data_res) return QUIT;
00472             } while(cfg.child("next_scenario") == NULL);
00473 
00474             if(cfg.child("next_scenario")) {
00475                 starting_pos = (*cfg.child("next_scenario"));
00476                 scenario = &starting_pos;
00477                 gamestate = game_state(starting_pos);
00478             } else {
00479                 return QUIT;
00480             }
00481         } else {
00482             scenario = game_config.find_child(type,"id",gamestate.scenario);
00483 
00484             if(io_type == IO_SERVER && scenario != NULL) {
00485                 starting_pos = *scenario;
00486                 scenario = &starting_pos;
00487 
00488                 // Tweaks sides to adapt controllers and descriptions.
00489                 const config::child_list& sides_list = starting_pos.get_children("side");
00490                 for(config::child_list::const_iterator side = sides_list.begin();
00491                         side != sides_list.end(); ++side) {
00492                     std::string id = (**side)["save_id"];
00493                     if(id.empty()) {
00494                         continue;
00495                     }
00496 
00497                     /* Update side info to match current_player info
00498                      * to allow it taking the side in next scenario
00499                      * and to be set in the players list on side server
00500                      */
00501                     controller_map::const_iterator ctr = controllers.find(id);
00502                     if(ctr != controllers.end()) {
00503                         player_info *player = gamestate.get_player(id);
00504                         if (player) {
00505                             (**side)["current_player"] = player->name;
00506                             //! @todo TODO : remove (see TODO line 276 in server/game.cpp)
00507                             (**side)["user_description"] = player->name;
00508                         }
00509                         (**side)["controller"] = ctr->second.controller;
00510                     }
00511                 }
00512 
00513                 // Sends scenario data
00514                 config cfg;
00515                 cfg.add_child("store_next_scenario", *scenario);
00516 
00517                 // Adds player information, and other state
00518                 // information, to the configuration object
00519                 assert(cfg.child("store_next_scenario") != NULL);
00520                 write_game(gamestate, *cfg.child("store_next_scenario"), WRITE_SNAPSHOT_ONLY);
00521                 network::send_data(cfg, 0, true);
00522             }
00523         }
00524 
00525         if(scenario != NULL) {
00526             // Update the label
00527             std::string oldlabel = gamestate.label;
00528             if (gamestate.abbrev.empty())
00529                 gamestate.label = (*scenario)["name"];
00530             else {
00531                 gamestate.label = std::string(gamestate.abbrev);
00532                 gamestate.label.append("-");
00533                 gamestate.label.append((*scenario)["name"]);
00534             }
00535 
00536             // If this isn't the last scenario, then save the game
00537             if(save_game_after_scenario) {
00538 
00539                 // For multiplayer, we want the save
00540                 // to contain the starting position.
00541                 // For campaigns however, this is the
00542                 // start-of-scenario save and the
00543                 // starting position needs to be empty,
00544                 // to force a reload of the scenario config.
00545                 if (gamestate.campaign_type == "multiplayer"){
00546                     gamestate.starting_pos = *scenario;
00547                 }
00548                 else{
00549                     gamestate.starting_pos = config();
00550                 }
00551 
00552                 bool retry = true;
00553 
00554                 while(retry) {
00555                     retry = false;
00556 
00557 #ifdef TINY_GUI
00558                     const int should_save = dialogs::get_save_name(disp,
00559                         _("Do you want to save your game?"),
00560                         _("Name:"),
00561                         &gamestate.label);
00562                     if(should_save == 0)
00563 #endif /* TINY_GUI */
00564                     {
00565                         try {
00566                             save_game(gamestate);
00567                         } catch(game::save_game_failed&) {
00568                             gui::show_error_message(disp, _("The game could not be saved"));
00569                             retry = true;
00570                         }
00571                     }
00572                 }
00573             }
00574 
00575             if (gamestate.campaign_type != "multiplayer"){
00576                 gamestate.starting_pos = *scenario;
00577             }
00578         }
00579 
00580         recorder.set_save_info(gamestate);
00581     }
00582 
00583     if (!gamestate.scenario.empty() && gamestate.scenario != "null") {
00584         std::string message = _("Unknown scenario: '$scenario|'");
00585         utils::string_map symbols;
00586         symbols["scenario"] = gamestate.scenario;
00587         message = utils::interpolate_variables_into_string(message, &symbols);
00588         gui::show_error_message(disp, message);
00589         return QUIT;
00590     }
00591 
00592     if (gamestate.campaign_type == "scenario"){
00593         if (preferences::delete_saves())
00594             clean_saves(gamestate.label);
00595     }
00596     return VICTORY;
00597 }
00598 

Generated by doxygen 1.5.5 on 23 May 2008 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs