00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
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
00054
00055
00056
00057
00058
00059
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
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
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
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
00195
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
00265
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
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
00294
00295
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
00324
00325 bool gamestatus::set_time_of_day(int newTime)
00326 {
00327
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
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
00375
00376 set_time_of_day((turn() - 1) % times_.size());
00377 }
00378
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
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
00482 load_recall_list(snapshot->get_children("player"));
00483
00484 } else {
00485
00486
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
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
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
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
00705
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
00713
00714
00715
00716
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
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
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
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
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
00952
00953
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
01033
01034
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
01145
01146
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()
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 }