gamestatus.cpp

Go to the documentation of this file.
00001 /* $Id: gamestatus.cpp 26501 2008-05-09 22:44:58Z mordante $ */
00002 /*
00003    Copyright (C) 2003 - 2008 by David White <dave@whitevine.net>
00004    Part of the Battle for Wesnoth Project http://www.wesnoth.org/
00005 
00006    This program is free software; you can redistribute it and/or modify
00007    it under the terms of the GNU General Public License version 2
00008    or at your option any later version.
00009    This program is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY.
00011 
00012    See the COPYING file for more details.
00013 */
00014 
00015 //! @file gamestatus.cpp
00016 //! Maintain status of a game, load&save games.
00017 
00018 #include "global.hpp"
00019 
00020 #include "filesystem.hpp"
00021 #include "game_config.hpp"
00022 #include "game_errors.hpp"
00023 #include "gamestatus.hpp"
00024 #include "gettext.hpp"
00025 #include "language.hpp"
00026 #include "log.hpp"
00027 #include "game_preferences.hpp"
00028 #include "statistics.hpp"
00029 #include "util.hpp"
00030 #include "wesconfig.h"
00031 #include "serialization/binary_or_text.hpp"
00032 #include "serialization/binary_wml.hpp"
00033 #include "serialization/parser.hpp"
00034 #include "serialization/string_utils.hpp"
00035 #include "wml_exception.hpp"
00036 
00037 #include <algorithm>
00038 #include <cstdio>
00039 #include <iostream>
00040 #include <iterator>
00041 #include <sstream>
00042 #include <sys/time.h>
00043 #include <time.h>
00044 
00045 #define DBG_NG lg::debug(lg::engine)
00046 #define LOG_NG lg::info(lg::engine)
00047 #define WRN_NG lg::warn(lg::engine)
00048 #define ERR_NG lg::err(lg::engine)
00049 
00050 #ifdef _WIN32
00051 #include <windows.h>
00052 
00053 //! conv_ansi_utf8()
00054 //!   - Convert a string between ANSI encoding (for Windows filename) and UTF-8
00055 //!  string &name
00056 //!     - filename to be converted
00057 //!  bool a2u
00058 //!     - if true, convert the string from ANSI to UTF-8.
00059 //!     - if false, reverse. (convert it from UTF-8 to ANSI)
00060 void conv_ansi_utf8(std::string &name, bool a2u) {
00061     int wlen = MultiByteToWideChar(a2u ? CP_ACP : CP_UTF8, 0,
00062                                    name.c_str(), -1, NULL, 0);
00063     if (wlen == 0) return;
00064     WCHAR *wc = new WCHAR[wlen];
00065     if (wc == NULL) return;
00066     if (MultiByteToWideChar(a2u ? CP_ACP : CP_UTF8, 0, name.c_str(), -1,
00067                             wc, wlen) == 0) {
00068         delete wc;
00069         return;
00070     }
00071     int alen = WideCharToMultiByte(!a2u ? CP_ACP : CP_UTF8, 0, wc, wlen,
00072                                    NULL, 0, NULL, NULL);
00073     if (alen == 0) {
00074         delete wc;
00075         return;
00076     }
00077     CHAR *ac = new CHAR[alen];
00078     if (ac == NULL) {
00079         delete wc;
00080         return;
00081     }
00082     WideCharToMultiByte(!a2u ? CP_ACP : CP_UTF8, 0, wc, wlen,
00083                         ac, alen, NULL, NULL);
00084     delete wc;
00085     if (ac == NULL) {
00086         return;
00087     }
00088     name = ac;
00089     delete ac;
00090 
00091     return;
00092 }
00093 
00094 void replace_underbar2space(std::string &name) {
00095     LOG_NG << "conv(A2U)-from:[" << name << "]" << std::endl;
00096     conv_ansi_utf8(name, true);
00097     LOG_NG << "conv(A2U)-to:[" << name << "]" << std::endl;
00098     LOG_NG << "replace_underbar2space-from:[" << name << "]" << std::endl;
00099     std::replace(name.begin(), name.end(), '_', ' ');
00100     LOG_NG << "replace_underbar2space-to:[" << name << "]" << std::endl;
00101 }
00102 
00103 static void replace_space2underbar(std::string &name) {
00104     LOG_NG << "conv(U2A)-from:[" << name << "]" << std::endl;
00105     conv_ansi_utf8(name, false);
00106     LOG_NG << "conv(U2A)-to:[" << name << "]" << std::endl;
00107     LOG_NG << "replace_underbar2space-from:[" << name << "]" << std::endl;
00108     std::replace(name.begin(), name.end(), ' ', '_');
00109     LOG_NG << "replace_underbar2space-to:[" << name << "]" << std::endl;
00110 }
00111 #else /* ! _WIN32 */
00112 void replace_underbar2space(std::string &name) {
00113     std::replace(name.begin(),name.end(),'_',' ');
00114 }
00115 static void replace_space2underbar(std::string &name) {
00116     std::replace(name.begin(),name.end(),' ','_');
00117 }
00118 #endif /* _WIN32 */
00119 
00120 static void extract_summary_from_config(config& cfg_save, config& cfg_summary);
00121 static void extract_summary_data_from_save(const game_state& gamestate, config& out);
00122 
00123 player_info* game_state::get_player(const std::string& id) {
00124     std::map< std::string, player_info >::iterator found = players.find(id);
00125     if (found == players.end()) {
00126         LOG_STREAM(warn, engine) << "player " << id << " does not exist.\n";
00127         return NULL;
00128     } else
00129         return &found->second;
00130 }
00131 
00132 time_of_day::time_of_day(const config& cfg)
00133                  : lawful_bonus(atoi(cfg["lawful_bonus"].c_str())),
00134                    bonus_modified(0),
00135                    image(cfg["image"]), name(cfg["name"]), id(cfg["id"]),
00136                    image_mask(cfg["mask"]),
00137                    red(atoi(cfg["red"].c_str())),
00138                    green(atoi(cfg["green"].c_str())),
00139                    blue(atoi(cfg["blue"].c_str())),
00140            sounds(cfg["sound"])
00141 {
00142 }
00143 
00144 void time_of_day::write(config& cfg) const
00145 {
00146     char buf[50];
00147     snprintf(buf,sizeof(buf),"%d",lawful_bonus);
00148     cfg["lawful_bonus"] = buf;
00149 
00150     snprintf(buf,sizeof(buf),"%d",red);
00151     cfg["red"] = buf;
00152 
00153     snprintf(buf,sizeof(buf),"%d",green);
00154     cfg["green"] = buf;
00155 
00156     snprintf(buf,sizeof(buf),"%d",blue);
00157     cfg["blue"] = buf;
00158 
00159     cfg["image"] = image;
00160     cfg["name"] = name;
00161     cfg["id"] = id;
00162     cfg["mask"] = image_mask;
00163 }
00164 
00165 static void parse_times(const config& cfg, std::vector<time_of_day>& normal_times)
00166 {
00167     const config::child_list& times = cfg.get_children("time");
00168     config::child_list::const_iterator t;
00169     for(t = times.begin(); t != times.end(); ++t) {
00170         normal_times.push_back(time_of_day(**t));
00171     }
00172 
00173     if(normal_times.empty())
00174     {
00175         // Make sure we have at least default time
00176         config dummy_cfg;
00177         normal_times.push_back(time_of_day(dummy_cfg));
00178     }
00179 }
00180 
00181 #ifdef __UNUSED__
00182 std::string generate_game_uuid()
00183 {
00184     struct timeval ts;
00185     std::stringstream uuid;
00186     gettimeofday(&ts, NULL);
00187 
00188     uuid << preferences::login() << "@" << ts.tv_sec << "." << ts.tv_usec;
00189 
00190     return uuid.str();
00191 }
00192 #endif
00193 
00194 //! Reads turns and time information from parameters.
00195 //! It sets random starting ToD and current_tod to config.
00196 gamestatus::gamestatus(const config& time_cfg, int num_turns, game_state* s_o_g) :
00197         teams(0),
00198         times_(),
00199         areas_(),
00200         turn_(1),
00201         numTurns_(num_turns),
00202         currentTime_(0),
00203         state_of_game_(s_o_g)
00204 {
00205     std::string turn_at = time_cfg["turn_at"];
00206     std::string current_tod = time_cfg["current_tod"];
00207     std::string random_start_time = time_cfg["random_start_time"];
00208     if (s_o_g)
00209     {
00210         turn_at = utils::interpolate_variables_into_string(turn_at, *s_o_g);
00211         current_tod = utils::interpolate_variables_into_string(current_tod, *s_o_g);
00212 
00213     }
00214 
00215     if(turn_at.empty() == false) {
00216         turn_ = atoi(turn_at.c_str());
00217     }
00218 
00219     parse_times(time_cfg,times_);
00220 
00221     set_start_ToD(const_cast<config&>(time_cfg),s_o_g);
00222 
00223     const config::child_list& times_range = time_cfg.get_children("time_area");
00224     for(config::child_list::const_iterator t = times_range.begin(); t != times_range.end(); ++t) {
00225         const std::vector<gamemap::location> locs = parse_location_range((**t)["x"],(**t)["y"]);
00226         area_time_of_day area;
00227         area.xsrc = (**t)["x"];
00228         area.ysrc = (**t)["y"];
00229         std::copy(locs.begin(),locs.end(),std::inserter(area.hexes,area.hexes.end()));
00230         parse_times(**t,area.times);
00231         areas_.push_back(area);
00232     }
00233 }
00234 
00235 void gamestatus::write(config& cfg) const
00236 {
00237     std::stringstream buf;
00238     buf << turn_;
00239     cfg["turn_at"] = buf.str();
00240     buf.str(std::string());
00241     buf << numTurns_;
00242     cfg["turns"] = buf.str();
00243     buf.str(std::string());
00244     buf << currentTime_;
00245     cfg["current_tod"] = buf.str();
00246 
00247     std::vector<time_of_day>::const_iterator t;
00248     for(t = times_.begin(); t != times_.end(); ++t) {
00249         t->write(cfg.add_child("time"));
00250     }
00251 
00252 
00253     for(std::vector<area_time_of_day>::const_iterator i = areas_.begin(); i != areas_.end(); ++i) {
00254         config& area = cfg.add_child("time_area");
00255         area["x"] = i->xsrc;
00256         area["y"] = i->ysrc;
00257         for(t = i->times.begin(); t != i->times.end(); ++t) {
00258             t->write(area.add_child("time"));
00259         }
00260 
00261     }
00262 }
00263 
00264 //! Returns time of day object in the turn.
00265 //! Correct time is calculated from current time.
00266 time_of_day gamestatus::get_time_of_day_turn(int nturn) const
00267 {
00268     VALIDATE(times_.size(), _("No time of day has been defined."));
00269 
00270     int time = (currentTime_ + nturn  - turn())% times_.size();
00271 
00272     if (time < 0)
00273     {
00274         time += times_.size();
00275     }
00276 
00277     return times_[time];
00278 }
00279 
00280 //! ~eturns time of day object for current turn.
00281 time_of_day gamestatus::get_time_of_day() const
00282 {
00283     VALIDATE(times_.size(), _("No time of day has been defined."));
00284 
00285     return times_[currentTime_];
00286 }
00287 
00288 time_of_day gamestatus::get_previous_time_of_day() const
00289 {
00290     return get_time_of_day_turn(turn()-1);
00291 }
00292 
00293 //! Returns time of day object in the turn.
00294 //! It first tries to look for specified.
00295 //! If no area time specified in location, it returns global time.
00296 time_of_day gamestatus::get_time_of_day(int illuminated, const gamemap::location& loc, int n_turn) const
00297 {
00298     time_of_day res = get_time_of_day_turn(n_turn);
00299 
00300     for(std::vector<area_time_of_day>::const_iterator i = areas_.begin(); i != areas_.end(); ++i) {
00301         if(i->hexes.count(loc) == 1) {
00302 
00303             VALIDATE(i->times.size(), _("No time of day has been defined."));
00304 
00305             res = i->times[(n_turn-1)%i->times.size()];
00306             break;
00307         }
00308     }
00309 
00310 
00311     if(illuminated) {
00312         res.bonus_modified=illuminated;
00313         res.lawful_bonus += illuminated;
00314     }
00315     return res;
00316 }
00317 
00318 time_of_day gamestatus::get_time_of_day(int illuminated, const gamemap::location& loc) const
00319 {
00320     return get_time_of_day(illuminated,loc,turn());
00321 }
00322 
00323 //! Sets global time of day in this turn.
00324 //! Time is a number between 0 and n-1, where n is number of ToDs.
00325 bool gamestatus::set_time_of_day(int newTime)
00326 {
00327     // newTime can come from network so have to take run time test
00328     if( newTime >= static_cast<int>(times_.size())
00329       || newTime < 0)
00330     {
00331         return false;
00332     }
00333 
00334     currentTime_ = newTime;
00335 
00336     return true;
00337 }
00338 
00339 bool gamestatus::is_start_ToD(const std::string& random_start_time)
00340 {
00341     return !random_start_time.empty()
00342             && utils::string_bool(random_start_time, true);
00343 }
00344 
00345 void gamestatus::set_start_ToD(config &level, game_state* s_o_g)
00346 {
00347     if (!level["current_tod"].empty())
00348     {
00349         set_time_of_day(atoi(level["current_tod"].c_str()));
00350         return;
00351     }
00352     std::string random_start_time = level["random_start_time"];
00353     if (s_o_g)
00354     {
00355         random_start_time = utils::interpolate_variables_into_string(random_start_time, *s_o_g);
00356     }
00357     if (gamestatus::is_start_ToD(random_start_time))
00358     {
00359         std::vector<std::string> start_strings =
00360             utils::split(random_start_time, ',', utils::STRIP_SPACES | utils::REMOVE_EMPTY);
00361 
00362         if (utils::string_bool(random_start_time,false))
00363         {
00364             // We had boolean value
00365             set_time_of_day(rand()%times_.size());
00366         }
00367         else
00368         {
00369             set_time_of_day(atoi(start_strings[rand()%start_strings.size()].c_str()) - 1);
00370         }
00371     }
00372     else
00373     {
00374         // We have to set right ToD for oldsaves
00375 
00376         set_time_of_day((turn() - 1) % times_.size());
00377     }
00378     // Setting ToD to level data
00379 
00380     std::stringstream buf;
00381     buf << currentTime_;
00382     level["current_tod"] = buf.str();
00383 
00384 }
00385 
00386 void gamestatus::next_time_of_day()
00387 {
00388     VALIDATE(times_.size(), _("No time of day has been defined."));
00389 
00390     currentTime_ = (currentTime_ + 1)%times_.size();
00391 }
00392 
00393 size_t gamestatus::turn() const
00394 {
00395     return turn_;
00396 }
00397 
00398 int gamestatus::number_of_turns() const
00399 {
00400     return numTurns_;
00401 }
00402 void gamestatus::modify_turns(const std::string& mod)
00403 {
00404     numTurns_ = maximum<int>(utils::apply_modifier(numTurns_,mod,0),-1);
00405 }
00406 void gamestatus::add_turns(int num)
00407 {
00408     numTurns_ = maximum<int>(numTurns_ + num,-1);
00409 }
00410 
00411 
00412 bool gamestatus::next_turn()
00413 {
00414     next_time_of_day();
00415     ++turn_;
00416     return numTurns_ == -1 || turn_ <= size_t(numTurns_);
00417 }
00418 
00419 static player_info read_player(const config* cfg)
00420 {
00421     player_info res;
00422 
00423     res.name = (*cfg)["name"];
00424 
00425     res.gold = atoi((*cfg)["gold"].c_str());
00426     res.gold_add = utils::string_bool((*cfg)["gold_add"]);
00427 
00428     const config::child_list& units = cfg->get_children("unit");
00429     for(config::child_list::const_iterator i = units.begin(); i != units.end(); ++i) {
00430         res.available_units.push_back(unit(**i,false));
00431     }
00432 
00433     res.can_recruit.clear();
00434 
00435     const std::string& can_recruit_str = (*cfg)["can_recruit"];
00436     if(can_recruit_str != "") {
00437         const std::vector<std::string> can_recruit = utils::split(can_recruit_str);
00438         std::copy(can_recruit.begin(),can_recruit.end(),std::inserter(res.can_recruit,res.can_recruit.end()));
00439     }
00440 
00441     return res;
00442 }
00443 
00444 game_state::game_state(const config& cfg, bool show_replay) :
00445         label(cfg["label"]),
00446         version(cfg["version"]),
00447         campaign_type(cfg["campaign_type"]),
00448         campaign_define(cfg["campaign_define"]),
00449         campaign_xtra_defines(utils::split(cfg["campaign_extra_defines"])),
00450         campaign(cfg["campaign"]),
00451         history(cfg["history"]),
00452         abbrev(cfg["abbrev"]),
00453         scenario(cfg["scenario"]),
00454         next_scenario(cfg["next_scenario"]),
00455         completion(cfg["completion"]),
00456         players(),
00457         scoped_variables(),
00458         wml_menu_items(),
00459         difficulty(cfg["difficulty"]),
00460         replay_data(),
00461         starting_pos(),
00462         snapshot(),
00463         last_selected(gamemap::location::null_location),
00464         rng_(cfg),
00465         variables(),
00466         temporaries()
00467 {
00468     log_scope("read_game");
00469 
00470     const config* snapshot = cfg.child("snapshot");
00471 
00472     // We have to load era id for MP games so they can load correct era.
00473 
00474 
00475     if ((snapshot != NULL) && (!snapshot->empty()) && (!show_replay)) {
00476 
00477         this->snapshot = *snapshot;
00478 
00479         rng_.seed_random(lexical_cast_default<unsigned>((*snapshot)["random_calls"]));
00480 
00481         // Midgame saves have the recall list stored in the snapshot.
00482         load_recall_list(snapshot->get_children("player"));
00483 
00484     } else {
00485         // Start of scenario save, replays and MP campaign network next scenario
00486         // have the recall list stored in root of the config.
00487         load_recall_list(cfg.get_children("player"));
00488     }
00489 
00490     std::cerr << "scenario: '" << scenario << "'\n";
00491     std::cerr << "next_scenario: '" << next_scenario << "'\n";
00492 
00493     if(difficulty.empty()) {
00494         difficulty = "NORMAL";
00495     }
00496 
00497     if(campaign_type.empty()) {
00498         campaign_type = "scenario";
00499     }
00500 
00501     const config* const vars = cfg.child("variables");
00502     if(vars != NULL) {
00503         set_variables(*vars);
00504     }
00505     set_menu_items(cfg.get_children("menu_item"));
00506 
00507     const config* const replay = cfg.child("replay");
00508     if(replay != NULL) {
00509         replay_data = *replay;
00510     }
00511 
00512     const config* replay_start = cfg.child("replay_start");
00513     if(replay_start != NULL) {
00514         starting_pos = *replay_start;
00515     }
00516 
00517     if(cfg.child("statistics")) {
00518         statistics::fresh_stats();
00519         statistics::read_stats(*cfg.child("statistics"));
00520     }
00521 }
00522 
00523 static void write_player(const player_info& player, config& cfg)
00524 {
00525     cfg["name"] = player.name;
00526 
00527     char buf[50];
00528     snprintf(buf,sizeof(buf),"%d",player.gold);
00529 
00530     cfg["gold"] = buf;
00531 
00532     cfg["gold_add"] = player.gold_add ? "true" : "false";
00533 
00534     for(std::vector<unit>::const_iterator i = player.available_units.begin();
00535         i != player.available_units.end(); ++i) {
00536         config new_cfg;
00537         i->write(new_cfg);
00538         cfg.add_child("unit",new_cfg);
00539         DBG_NG << "added unit '" << new_cfg["id"] << "' to player '" << player.name << "'\n";
00540     }
00541 
00542     std::stringstream can_recruit;
00543     std::copy(player.can_recruit.begin(),player.can_recruit.end(),std::ostream_iterator<std::string>(can_recruit,","));
00544     std::string can_recruit_str = can_recruit.str();
00545 
00546     // Remove the trailing comma
00547     if(can_recruit_str.empty() == false) {
00548         can_recruit_str.resize(can_recruit_str.size()-1);
00549     }
00550 
00551     cfg["can_recruit"] = can_recruit_str;
00552 }
00553 
00554 static void write_player(config_writer &out, const player_info& player)
00555 {
00556     out.write_key_val("name", player.name);
00557 
00558     char buf[50];
00559     snprintf(buf,sizeof(buf),"%d",player.gold);
00560 
00561     out.write_key_val("gold", buf);
00562 
00563     for(std::vector<unit>::const_iterator i = player.available_units.begin();
00564         i != player.available_units.end(); ++i) {
00565         config new_cfg;
00566         i->write(new_cfg);
00567         out.write_child("unit",new_cfg);
00568         DBG_NG << "added unit '" << new_cfg["id"] << "' to player '" << player.name << "'\n";
00569     }
00570 
00571     std::stringstream can_recruit;
00572     std::copy(player.can_recruit.begin(),player.can_recruit.end(),std::ostream_iterator<std::string>(can_recruit,","));
00573     std::string can_recruit_str = can_recruit.str();
00574 
00575     // Remove the trailing comma
00576     if(can_recruit_str.empty() == false) {
00577         can_recruit_str.resize(can_recruit_str.size()-1);
00578     }
00579 
00580     out.write_key_val("can_recruit", can_recruit_str);
00581 }
00582 
00583 
00584 //! @deprecated, use other write_game below.
00585 void write_game(const game_state& gamestate, config& cfg, WRITE_GAME_MODE mode)
00586 {
00587     log_scope("write_game");
00588     cfg["label"] = gamestate.label;
00589     cfg["history"] = gamestate.history;
00590     cfg["abbrev"] = gamestate.abbrev;
00591     cfg["version"] = game_config::version;
00592 
00593     cfg["scenario"] = gamestate.scenario;
00594     cfg["next_scenario"] = gamestate.next_scenario;
00595 
00596     cfg["completion"] = gamestate.completion;
00597 
00598     cfg["campaign"] = gamestate.campaign;
00599 
00600     cfg["campaign_type"] = gamestate.campaign_type;
00601 
00602     cfg["difficulty"] = gamestate.difficulty;
00603 
00604     cfg["campaign_define"] = gamestate.campaign_define;
00605     cfg["campaign_extra_defines"] = utils::join(gamestate.campaign_xtra_defines);
00606 
00607     cfg["random_seed"] = lexical_cast<std::string>(gamestate.rng().get_random_seed());
00608     cfg["random_calls"] = lexical_cast<std::string>(gamestate.rng().get_random_calls());
00609 
00610     cfg.add_child("variables",gamestate.get_variables());
00611 
00612     for(std::map<std::string, wml_menu_item *>::const_iterator j=gamestate.wml_menu_items.begin();
00613         j!=gamestate.wml_menu_items.end(); ++j) {
00614         config new_cfg;
00615         new_cfg["id"]=j->first;
00616         new_cfg["image"]=j->second->image;
00617         new_cfg["description"]=j->second->description;
00618         new_cfg["needs_select"]= (j->second->needs_select) ? "yes" : "no";
00619         if(!j->second->show_if.empty())
00620             new_cfg.add_child("show_if", j->second->show_if);
00621         if(!j->second->filter_location.empty())
00622             new_cfg.add_child("filter_location", j->second->filter_location);
00623         if(!j->second->command.empty())
00624             new_cfg.add_child("command", j->second->command);
00625         cfg.add_child("menu_item", new_cfg);
00626     }
00627 
00628     for(std::map<std::string, player_info>::const_iterator i=gamestate.players.begin();
00629         i!=gamestate.players.end(); ++i) {
00630         config new_cfg;
00631         write_player(i->second, new_cfg);
00632         new_cfg["save_id"]=i->first;
00633         cfg.add_child("player", new_cfg);
00634     }
00635 
00636     if(mode == WRITE_FULL_GAME) {
00637         if(gamestate.replay_data.child("replay") == NULL) {
00638             cfg.add_child("replay",gamestate.replay_data);
00639         }
00640 
00641         cfg.add_child("snapshot",gamestate.snapshot);
00642         cfg.add_child("replay_start",gamestate.starting_pos);
00643         cfg.add_child("statistics",statistics::write_stats());
00644     }
00645 }
00646 
00647 void write_game(config_writer &out, const game_state& gamestate, WRITE_GAME_MODE mode)
00648 {
00649     log_scope("write_game");
00650 
00651     out.write_key_val("label", gamestate.label);
00652     out.write_key_val("history", gamestate.history);
00653     out.write_key_val("abbrev", gamestate.abbrev);
00654     out.write_key_val("version", game_config::version);
00655     out.write_key_val("scenario", gamestate.scenario);
00656     out.write_key_val("next_scenario", gamestate.next_scenario);
00657     out.write_key_val("completion", gamestate.completion);
00658     out.write_key_val("campaign", gamestate.campaign);
00659     out.write_key_val("campaign_type", gamestate.campaign_type);
00660     out.write_key_val("difficulty", gamestate.difficulty);
00661     out.write_key_val("campaign_define", gamestate.campaign_define);
00662     out.write_key_val("campaign_extra_defines", utils::join(gamestate.campaign_xtra_defines));
00663     out.write_key_val("random_seed", lexical_cast<std::string>(gamestate.rng().get_random_seed()));
00664     out.write_key_val("random_calls", lexical_cast<std::string>(gamestate.rng().get_random_calls()));
00665     out.write_child("variables", gamestate.get_variables());
00666 
00667     for(std::map<std::string, wml_menu_item *>::const_iterator j=gamestate.wml_menu_items.begin();
00668         j!=gamestate.wml_menu_items.end(); ++j) {
00669         out.open_child("menu_item");
00670         out.write_key_val("id", j->first);
00671         out.write_key_val("image", j->second->image);
00672         out.write_key_val("description", j->second->description);
00673         out.write_key_val("needs_select", (j->second->needs_select) ? "yes" : "no");
00674         if(!j->second->show_if.empty())
00675             out.write_child("show_if", j->second->show_if);
00676         if(!j->second->filter_location.empty())
00677             out.write_child("filter_location", j->second->filter_location);
00678         if(!j->second->command.empty())
00679             out.write_child("command", j->second->command);
00680         out.close_child("menu_item");
00681     }
00682 
00683     for(std::map<std::string, player_info>::const_iterator i=gamestate.players.begin();
00684         i!=gamestate.players.end(); ++i) {
00685         out.open_child("player");
00686         out.write_key_val("save_id", i->first);
00687         write_player(out, i->second);
00688         out.close_child("player");
00689     }
00690 
00691     if(mode == WRITE_FULL_GAME) {
00692         if(gamestate.replay_data.child("replay") == NULL) {
00693             out.write_child("replay", gamestate.replay_data);
00694         }
00695 
00696         out.write_child("snapshot",gamestate.snapshot);
00697         out.write_child("replay_start",gamestate.starting_pos);
00698         out.open_child("statistics");
00699         statistics::write_stats(out);
00700         out.close_child("statistics");
00701     }
00702 }
00703 
00704 //! A structure for comparing to save_info objects based on their modified time.
00705 //! If the times are equal, will order based on the name.
00706 struct save_info_less_time {
00707     bool operator()(const save_info& a, const save_info& b) const {
00708         if (a.time_modified > b.time_modified) {
00709                 return true;
00710         } else if (a.time_modified < b.time_modified) {
00711             return false;
00712         // Special funky case; for files created in the same second,
00713         // a replay file sorts less than a non-replay file.  Prevents
00714         // a timing-dependent bug where it may look like, at the end
00715         // of a scenario, the replay and the autosave for the next
00716         // scenario are displayed in the wrong order.
00717         } else if (a.name.find(_(" replay"))==std::string::npos && b.name.find(_(" replay"))!=std::string::npos) {
00718             return true;
00719         } else if (a.name.find(_(" replay"))!=std::string::npos && b.name.find(_(" replay"))==std::string::npos) {
00720             return false;
00721         } else {
00722             return  a.name > b.name;
00723         }
00724     }
00725 };
00726 
00727 std::vector<save_info> get_saves_list(const std::string *dir, const std::string* filter)
00728 {
00729     // Don't use a reference, it seems to break on arklinux with GCC-4.3.
00730     const std::string saves_dir = (dir) ? *dir : get_saves_dir();
00731 
00732     std::vector<std::string> saves;
00733     get_files_in_dir(saves_dir,&saves);
00734 
00735     std::vector<save_info> res;
00736     for(std::vector<std::string>::iterator i = saves.begin(); i != saves.end(); ++i) {
00737         if(filter && std::search(i->begin(), i->end(), filter->begin(), filter->end()) == i->end()) {
00738             continue;
00739         }
00740 
00741         const time_t modified = file_create_time(saves_dir + "/" + *i);
00742 
00743         replace_underbar2space(*i);
00744         res.push_back(save_info(*i,modified));
00745     }
00746 
00747     std::sort(res.begin(),res.end(),save_info_less_time());
00748 
00749     return res;
00750 }
00751 
00752 bool save_game_exists(const std::string& name)
00753 {
00754     std::string fname = name;
00755     replace_space2underbar(fname);
00756 
00757     if(preferences::compress_saves()) {
00758         fname += ".gz";
00759     }
00760 
00761     return file_exists(get_saves_dir() + "/" + fname);
00762 }
00763 
00764 void delete_game(const std::string& name)
00765 {
00766     std::string modified_name = name;
00767     replace_space2underbar(modified_name);
00768 
00769     remove((get_saves_dir() + "/" + name).c_str());
00770     remove((get_saves_dir() + "/" + modified_name).c_str());
00771 }
00772 
00773 void read_save_file(const std::string& name, config& cfg, std::string* error_log)
00774 {
00775     std::string modified_name = name;
00776     replace_space2underbar(modified_name);
00777 
00778     // Try reading the file both with and without underscores
00779     scoped_istream file_stream = istream_file(get_saves_dir() + "/" + modified_name);
00780     if (file_stream->fail())
00781         file_stream = istream_file(get_saves_dir() + "/" + name);
00782 
00783     cfg.clear();
00784     try{
00785         if(is_gzip_file(name)) {
00786             read_gz(cfg, *file_stream, error_log);
00787         } else {
00788             detect_format_and_read(cfg, *file_stream, error_log);
00789         }
00790     } catch (config::error &err)
00791     {
00792         std::cerr << err.message;
00793         throw game::load_game_failed();
00794     }
00795 
00796     if(cfg.empty()) {
00797         std::cerr << "Could not parse file data into config\n";
00798         throw game::load_game_failed();
00799     }
00800 }
00801 
00802 static void copy_era(config &cfg)
00803 {
00804     if (cfg.child("replay_start")
00805         && cfg.child("replay_start")->child("era")
00806         && cfg.child("snapshot"))
00807     {
00808         config *snapshot = cfg.child("snapshot");
00809         snapshot->add_child("era",*cfg.child("replay_start")->child("era"));
00810     }
00811 }
00812 
00813 void load_game(const std::string& name, game_state& gamestate, std::string* error_log)
00814 {
00815     log_scope("load_game");
00816 
00817     config cfg;
00818     read_save_file(name,cfg,error_log);
00819     copy_era(cfg);
00820 
00821     gamestate = game_state(cfg);
00822 }
00823 
00824 void load_game_summary(const std::string& name, config& cfg_summary, std::string* error_log){
00825     log_scope("load_game_summary");
00826 
00827     config cfg;
00828     read_save_file(name,cfg,error_log);
00829 
00830     extract_summary_from_config(cfg, cfg_summary);
00831 }
00832 
00833 // Throws game::save_game_failed
00834 scoped_ostream open_save_game(const std::string &label)
00835 {
00836     std::string name = label;
00837     replace_space2underbar(name);
00838 
00839     try {
00840         return scoped_ostream(ostream_file(get_saves_dir() + "/" + name));
00841     } catch(io_exception& e) {
00842         throw game::save_game_failed(e.what());
00843     }
00844 }
00845 
00846 void finish_save_game(config_writer &out, const game_state& gamestate, const std::string &label)
00847 {
00848     std::string name = label;
00849     std::replace(name.begin(),name.end(),' ','_');
00850     std::string fname(get_saves_dir() + "/" + name);
00851 
00852     try {
00853         if(!out.good()) {
00854             throw game::save_game_failed(_("Could not write to file"));
00855         }
00856 
00857         config& summary = save_summary(label);
00858         extract_summary_data_from_save(gamestate,summary);
00859         const int mod_time = static_cast<int>(file_create_time(fname));
00860         summary["mod_time"] = str_cast(mod_time);
00861         write_save_index();
00862     } catch(io_exception& e) {
00863         throw game::save_game_failed(e.what());
00864     }
00865 }
00866 
00867 // Throws game::save_game_failed
00868 void save_game(const game_state& gamestate)
00869 {
00870     std::string filename = gamestate.label;
00871     if(preferences::compress_saves()) {
00872         filename += ".gz";
00873     }
00874 
00875     scoped_ostream os(open_save_game(filename));
00876     config_writer out(*os, preferences::compress_saves(), PACKAGE);
00877     write_game(out, gamestate);
00878     finish_save_game(out, gamestate, gamestate.label);
00879 }
00880 
00881 namespace {
00882 bool save_index_loaded = false;
00883 config save_index_cfg;
00884 }
00885 
00886 static config& save_index()
00887 {
00888     if(save_index_loaded == false) {
00889         try {
00890             scoped_istream stream = istream_file(get_save_index_file());
00891             detect_format_and_read(save_index_cfg, *stream);
00892         } catch(io_exception& e) {
00893             std::cerr << "error reading save index: '" << e.what() << "'\n";
00894         } catch(config::error&) {
00895             std::cerr << "error parsing save index config file\n";
00896             save_index_cfg.clear();
00897         }
00898 
00899         save_index_loaded = true;
00900     }
00901 
00902     return save_index_cfg;
00903 }
00904 
00905 config& save_summary(const std::string& save)
00906 {
00907     config& cfg = save_index();
00908     config* res = cfg.find_child("save","save",save);
00909     if(res == NULL) {
00910         res = &cfg.add_child("save");
00911         (*res)["save"] = save;
00912     }
00913 
00914     return *res;
00915 }
00916 
00917 void write_save_index()
00918 {
00919     log_scope("write_save_index()");
00920     try {
00921         scoped_ostream stream = ostream_file(get_save_index_file());
00922         write_compressed(*stream, save_index());
00923     } catch(io_exception& e) {
00924         std::cerr << "error writing to save index file: '" << e.what() << "'\n";
00925     }
00926 }
00927 
00928 void extract_summary_data_from_save(const game_state& gamestate, config& out)
00929 {
00930     const bool has_replay = gamestate.replay_data.empty() == false;
00931     const bool has_snapshot = gamestate.snapshot.child("side") != NULL;
00932 
00933     out["replay"] = has_replay ? "yes" : "no";
00934     out["snapshot"] = has_snapshot ? "yes" : "no";
00935 
00936     out["label"] = gamestate.label;
00937     out["campaign"] = gamestate.campaign;
00938     out["campaign_type"] = gamestate.campaign_type;
00939     out["scenario"] = gamestate.scenario;
00940     out["difficulty"] = gamestate.difficulty;
00941     out["version"] = gamestate.version;
00942     out["corrupt"] = "";
00943 
00944     if(has_snapshot) {
00945         out["turn"] = gamestate.snapshot["turn_at"];
00946         if(gamestate.snapshot["turns"] != "-1") {
00947             out["turn"] = out["turn"].str() + "/" + gamestate.snapshot["turns"].str();
00948         }
00949     }
00950 
00951     // Find the first human leader so we can display their icon in the load menu.
00952 
00953     //! @todo Ideally we should grab all leaders if there's more than 1 human player?
00954     std::string leader;
00955 
00956     for(std::map<std::string, player_info>::const_iterator p = gamestate.players.begin();
00957         p!=gamestate.players.end(); ++p) {
00958         for(std::vector<unit>::const_iterator u = p->second.available_units.begin(); u != p->second.available_units.end(); ++u) {
00959             if(u->can_recruit()) {
00960                 leader = u->type_id();
00961             }
00962         }
00963     }
00964 
00965     bool shrouded = false;
00966 
00967     if(leader == "") {
00968         const config& snapshot = has_snapshot ? gamestate.snapshot : gamestate.starting_pos;
00969         const config::child_list& sides = snapshot.get_children("side");
00970         for(config::child_list::const_iterator s = sides.begin(); s != sides.end() && leader.empty(); ++s) {
00971 
00972             if((**s)["controller"] != "human") {
00973                 continue;
00974             }
00975 
00976             if(utils::string_bool((**s)["shroud"])) {
00977                 shrouded = true;
00978             }
00979 
00980             const config::child_list& units = (**s).get_children("unit");
00981             for(config::child_list::const_iterator u = units.begin(); u != units.end(); ++u) {
00982                 if(utils::string_bool( (**u)["canrecruit"], false) == true) {
00983                     leader = (**u)["id"];
00984                     break;
00985                 }
00986             }
00987         }
00988     }
00989 
00990     out["leader"] = leader;
00991     out["map_data"] = "";
00992 
00993     if(!shrouded) {
00994         if(has_snapshot) {
00995             if(gamestate.snapshot.find_child("side","shroud","yes") == NULL) {
00996                 out["map_data"] = gamestate.snapshot["map_data"];
00997             }
00998         } else if(has_replay) {
00999             if(gamestate.starting_pos.find_child("side","shroud","yes") == NULL) {
01000                 out["map_data"] = gamestate.starting_pos["map_data"];
01001             }
01002         }
01003     }
01004 }
01005 
01006 void extract_summary_from_config(config& cfg_save, config& cfg_summary)
01007 {
01008     const config* cfg_snapshot = cfg_save.child("snapshot");
01009     const config* cfg_replay_start = cfg_save.child("replay_start");
01010 
01011     const bool has_replay = cfg_save.child("replay") != NULL;
01012     const bool has_snapshot = (cfg_snapshot != NULL) && (cfg_snapshot->child("side") != NULL);
01013 
01014     cfg_summary["replay"] = has_replay ? "yes" : "no";
01015     cfg_summary["snapshot"] = has_snapshot ? "yes" : "no";
01016 
01017     cfg_summary["label"] = cfg_save["label"];
01018     cfg_summary["campaign_type"] = cfg_save["campaign_type"];
01019     cfg_summary["scenario"] = cfg_save["scenario"];
01020     cfg_summary["campaign"] = cfg_save["campaign"];
01021     cfg_summary["difficulty"] = cfg_save["difficulty"];
01022     cfg_summary["version"] = cfg_save["version"];
01023     cfg_summary["corrupt"] = "";
01024 
01025     if(has_snapshot) {
01026         cfg_summary["turn"] = (*cfg_snapshot)["turn_at"];
01027         if((*cfg_snapshot)["turns"] != "-1") {
01028             cfg_summary["turn"] = cfg_summary["turn"].str() + "/" + (*cfg_snapshot)["turns"].str();
01029         }
01030     }
01031 
01032     // Find the first human leader so we can display their icon in the load menu.
01033 
01034     //! @todo Ideally we should grab all leaders if there's more than 1 human player?
01035     std::string leader;
01036 
01037     const config::child_list& players = cfg_save.get_children("player");
01038 
01039     for(config::child_list::const_iterator i = players.begin(); i != players.end(); ++i) {
01040         if (utils::string_bool( (**i)["canrecruit"], false) == true){
01041             leader = (**i)["save_id"];
01042         }
01043     }
01044 
01045     bool shrouded = false;
01046 
01047     if(leader == "") {
01048         const config* snapshot = has_snapshot ? cfg_snapshot : cfg_replay_start;
01049         if (snapshot != NULL){
01050             const config::child_list& sides = snapshot->get_children("side");
01051             for(config::child_list::const_iterator s = sides.begin(); s != sides.end() && leader.empty(); ++s) {
01052 
01053                 if((**s)["controller"] != "human") {
01054                     continue;
01055                 }
01056 
01057                 if(utils::string_bool((**s)["shroud"])) {
01058                     shrouded = true;
01059                 }
01060 
01061                 const config::child_list& units = (**s).get_children("unit");
01062                 for(config::child_list::const_iterator u = units.begin(); u != units.end(); ++u) {
01063                     if(utils::string_bool( (**u)["canrecruit"], false) == true) {
01064                         leader = (**u)["id"];
01065                         break;
01066                     }
01067                 }
01068             }
01069         }
01070     }
01071 
01072     cfg_summary["leader"] = leader;
01073     cfg_summary["map_data"] = "";
01074 
01075     if(!shrouded) {
01076         if(has_snapshot) {
01077             if(cfg_snapshot->find_child("side","shroud","yes") == NULL) {
01078                 cfg_summary["map_data"] = (*cfg_snapshot)["map_data"];
01079             }
01080         } else if(has_replay) {
01081             if(cfg_replay_start->find_child("side","shroud","yes") == NULL) {
01082                 cfg_summary["map_data"] = (*cfg_replay_start)["map_data"];
01083             }
01084         }
01085     }
01086 }
01087 
01088 t_string& game_state::get_variable(const std::string& key)
01089 {
01090     return variable_info(key, true, variable_info::TYPE_SCALAR).as_scalar();
01091 }
01092 
01093 const t_string& game_state::get_variable_const(const std::string& key) const
01094 {
01095     variable_info to_get(key, false, variable_info::TYPE_SCALAR);
01096     if(!to_get.is_valid) return temporaries[key];
01097     return to_get.as_scalar();
01098 }
01099 
01100 config& game_state::get_variable_cfg(const std::string& key)
01101 {
01102     return variable_info(key, true, variable_info::TYPE_CONTAINER).as_container();
01103 }
01104 
01105 variable_info::array_range game_state::get_variable_cfgs(const std::string& key)
01106 {
01107     return variable_info(key, true, variable_info::TYPE_ARRAY).as_array();
01108 }
01109 
01110 void game_state::set_variable(const std::string& key, const t_string& value)
01111 {
01112     get_variable(key) = value;
01113 }
01114 
01115 config& game_state::add_variable_cfg(const std::string& key, const config& value)
01116 {
01117     variable_info to_add(key, true, variable_info::TYPE_ARRAY);
01118     return to_add.vars->add_child(to_add.key, value);
01119 }
01120 
01121 void game_state::clear_variable_cfg(const std::string& varname)
01122 {
01123     variable_info to_clear(varname, false, variable_info::TYPE_CONTAINER);
01124     if(!to_clear.is_valid) return;
01125     if(to_clear.explicit_index) {
01126         to_clear.vars->remove_child(to_clear.key, to_clear.index);
01127     } else {
01128         to_clear.vars->clear_children(to_clear.key);
01129     }
01130 }
01131 
01132 void game_state::clear_variable(const std::string& varname)
01133 {
01134     variable_info to_clear(varname, false);
01135     if(!to_clear.is_valid) return;
01136     if(to_clear.explicit_index) {
01137         to_clear.vars->remove_child(to_clear.key, to_clear.index);
01138     } else {
01139         to_clear.vars->clear_children(to_clear.key);
01140         to_clear.vars->values.erase(to_clear.key);
01141     }
01142 }
01143 
01144 //! Loads the recall list.
01145 //!
01146 //! @param players      Reference to the players section to load.
01147 void game_state::load_recall_list(const config::child_list& players)
01148 {
01149     if(!players.empty()) {
01150         for(config::child_list::const_iterator i = players.begin(); i != players.end(); ++i) {
01151             std::string save_id = (**i)["save_id"];
01152 
01153             if(save_id.empty()) {
01154                 std::cerr << "Corrupted player entry: NULL save_id" << std::endl;
01155             } else {
01156                 player_info player = read_player(*i);
01157                 this->players.insert(std::pair<std::string, player_info>(save_id,player));
01158             }
01159         }
01160     }
01161 }
01162 
01163 static void clear_wmi(std::map<std::string, wml_menu_item*>& gs_wmi) {
01164     std::map<std::string, wml_menu_item*>::iterator itor = gs_wmi.begin();
01165     for(itor = gs_wmi.begin(); itor != gs_wmi.end(); ++itor) {
01166         delete itor->second;
01167     }
01168     gs_wmi.clear();
01169 }
01170 
01171 game_state::game_state(const game_state& state) : variable_set(/*silences gcc warning*/)
01172 {
01173     *this = state;
01174 }
01175 
01176 game_state& game_state::operator=(const game_state& state)
01177 {
01178     if(this == &state) {
01179         return *this;
01180     }
01181 
01182     history = state.history;
01183     abbrev = state.abbrev;
01184     label = state.label;
01185     version = state.version;
01186     campaign_type = state.campaign_type;
01187     campaign_define = state.campaign_define;
01188     campaign_xtra_defines = state.campaign_xtra_defines;
01189     campaign = state.campaign;
01190     scenario = state.scenario;
01191     completion = state.completion;
01192     rng_ = state.rng_;
01193     players = state.players;
01194     scoped_variables = state.scoped_variables;
01195 
01196     clear_wmi(wml_menu_items);
01197     std::map<std::string, wml_menu_item*>::const_iterator itor;
01198     for (itor = state.wml_menu_items.begin(); itor != state.wml_menu_items.end(); ++itor) {
01199         wml_menu_item*& mref = wml_menu_items[itor->first];
01200         mref = new wml_menu_item(*(itor->second));
01201     }
01202 
01203     difficulty = state.difficulty;
01204     replay_data = state.replay_data;
01205     starting_pos = state.starting_pos;
01206     snapshot = state.snapshot;
01207     last_selected = state.last_selected;
01208     set_variables(state.get_variables());
01209 
01210     return *this;
01211 }
01212 
01213 game_state::~game_state() {
01214     clear_wmi(wml_menu_items);
01215 }
01216 
01217 void game_state::set_variables(const config& vars) {
01218     if(!variables.empty()) {
01219         WRN_NG << "clobbering the game_state variables\n";
01220         WRN_NG << variables;
01221     }
01222     variables = vars;
01223 }
01224 
01225 void game_state::set_menu_items(const config::child_list& menu_items) {
01226     clear_wmi(wml_menu_items);
01227     for(config::const_child_iterator i=menu_items.begin(); i != menu_items.end(); ++i) {
01228         const std::string& id = (**i)["id"].base_str();
01229         wml_menu_item*& mref = wml_menu_items[id];
01230         if(mref == NULL) {
01231             mref = new wml_menu_item(id, *i);
01232         } else {
01233             WRN_NG << "duplicate menu item (" << id << ") while loading gamestate\n";
01234         }
01235     }
01236 }
01237 
01238 void player_info::debug(){
01239     LOG_NG << "Debugging player\n";
01240     LOG_NG << "\tName: " << name << "\n";
01241     LOG_NG << "\tGold: " << gold << "\n";
01242     LOG_NG << "\tAvailable units:\n";
01243     for (std::vector<unit>::const_iterator u = available_units.begin(); u != available_units.end(); u++){
01244         LOG_NG << "\t\t" + u->name() + "\n";
01245     }
01246     LOG_NG << "\tEnd available units\n";
01247 }
01248 
01249 wml_menu_item::wml_menu_item(const std::string& id, const config* cfg) :
01250         name(),
01251         image(),
01252         description(),
01253         needs_select(false),
01254         show_if(),
01255         filter_location(),
01256         command()
01257 
01258 {
01259     std::stringstream temp;
01260     temp << "menu item";
01261     if(!id.empty()) {
01262         temp << ' ' << id;
01263     }
01264     name = temp.str();
01265     if(cfg != NULL) {
01266         image = (*cfg)["image"];
01267         description = (*cfg)["description"];
01268         needs_select = utils::string_bool((*cfg)["needs_select"], false);
01269         config const* temp;
01270         if((temp = (*cfg).child("show_if")) != NULL) show_if = *temp;
01271         if((temp = (*cfg).child("filter_location")) != NULL) filter_location = *temp;
01272         if((temp = (*cfg).child("command")) != NULL) command = *temp;
01273     }
01274 }

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