00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include "global.hpp"
00021
00022 #include "actions.hpp"
00023 #include "ai_interface.hpp"
00024 #include "dialogs.hpp"
00025 #include "game_display.hpp"
00026 #include "game_preferences.hpp"
00027 #include "filesystem.hpp"
00028 #include "game_config.hpp"
00029 #include "game_events.hpp"
00030 #include "log.hpp"
00031 #include "map_label.hpp"
00032 #include "menu_events.hpp"
00033 #include "pathfind.hpp"
00034 #include "replay.hpp"
00035 #include "show_dialog.hpp"
00036 #include "sound.hpp"
00037 #include "statistics.hpp"
00038 #include "unit_display.hpp"
00039 #include "util.hpp"
00040 #include "wesconfig.h"
00041 #include "serialization/binary_or_text.hpp"
00042
00043 #include <cassert>
00044 #include <cstdio>
00045 #include <cstdlib>
00046 #include <deque>
00047 #include <iostream>
00048 #include <set>
00049 #include <sstream>
00050
00051 #define DBG_REPLAY LOG_STREAM(debug, replay)
00052 #define LOG_REPLAY LOG_STREAM(info, replay)
00053 #define WRN_REPLAY LOG_STREAM(warn, replay)
00054 #define ERR_REPLAY LOG_STREAM(err, replay)
00055
00056 std::string replay::last_replay_error;
00057
00058
00059
00060 static void verify(const unit_map& units, const config& cfg) {
00061 std::stringstream errbuf;
00062 LOG_REPLAY << "verifying unit structure...\n";
00063
00064 const size_t nunits = lexical_cast_default<size_t>(cfg["num_units"]);
00065 if(nunits != units.size()) {
00066 errbuf << "SYNC VERIFICATION FAILED: number of units from data source differ: "
00067 << nunits << " according to data source. " << units.size() << " locally\n";
00068
00069 std::set<gamemap::location> locs;
00070 const config::child_list& items = cfg.get_children("unit");
00071 for(config::child_list::const_iterator i = items.begin(); i != items.end(); ++i) {
00072 const gamemap::location loc(**i, game_events::get_state_of_game());
00073 locs.insert(loc);
00074
00075 if(units.count(loc) == 0) {
00076 errbuf << "data source says there is a unit at "
00077 << loc << " but none found locally\n";
00078 }
00079 }
00080
00081 for(unit_map::const_iterator j = units.begin(); j != units.end(); ++j) {
00082 if(locs.count(j->first) == 0) {
00083 errbuf << "local unit at " << j->first
00084 << " but none in data source\n";
00085 }
00086 }
00087 replay::throw_error(errbuf.str());
00088 errbuf.clear();
00089 }
00090
00091 const config::child_list& items = cfg.get_children("unit");
00092 for(config::child_list::const_iterator i = items.begin(); i != items.end(); ++i) {
00093 const gamemap::location loc(**i, game_events::get_state_of_game());
00094 const unit_map::const_iterator u = units.find(loc);
00095 if(u == units.end()) {
00096 errbuf << "SYNC VERIFICATION FAILED: data source says there is a '"
00097 << (**i)["type"] << "' (side " << (**i)["side"] << ") at "
00098 << loc << " but there is no local record of it\n";
00099 replay::throw_error(errbuf.str());
00100 errbuf.clear();
00101 }
00102
00103 config cfg;
00104 u->second.write(cfg);
00105
00106 bool is_ok = true;
00107 static const std::string fields[] = {"type","hitpoints","experience","side",""};
00108 for(const std::string* str = fields; str->empty() == false; ++str) {
00109 if(cfg[*str] != (**i)[*str]) {
00110 errbuf << "ERROR IN FIELD '" << *str << "' for unit at "
00111 << loc << " data source: '" << (**i)[*str]
00112 << "' local: '" << cfg[*str] << "'\n";
00113 is_ok = false;
00114 }
00115 }
00116
00117 if(!is_ok) {
00118 errbuf << "(SYNC VERIFICATION FAILED)\n";
00119 replay::throw_error(errbuf.str());
00120 errbuf.clear();
00121 }
00122 }
00123
00124 LOG_REPLAY << "verification passed\n";
00125 }
00126
00127 namespace {
00128 const unit_map* unit_map_ref = NULL;
00129 }
00130
00131 static void verify_units(const config& cfg)
00132 {
00133 if(unit_map_ref != NULL) {
00134 verify(*unit_map_ref,cfg);
00135 }
00136 }
00137
00138 verification_manager::verification_manager(const unit_map& units)
00139 {
00140 unit_map_ref = &units;
00141 }
00142
00143 verification_manager::~verification_manager()
00144 {
00145 unit_map_ref = NULL;
00146 }
00147
00148
00149
00150
00151 replay recorder;
00152
00153 replay::replay() : pos_(0), current_(NULL), skip_(0)
00154 {}
00155
00156 replay::replay(const config& cfg) : cfg_(cfg), pos_(0), current_(NULL), skip_(0)
00157 {}
00158
00159 void replay::throw_error(const std::string& msg)
00160 {
00161 ERR_REPLAY << msg;
00162 last_replay_error = msg;
00163 if (!game_config::ignore_replay_errors) throw replay::error(msg);
00164 }
00165
00166 void replay::set_save_info(const game_state& save)
00167 {
00168 saveInfo_ = save;
00169 }
00170
00171
00172 void replay::set_save_info_completion(const std::string &st)
00173
00174
00175
00176 {
00177 saveInfo_.completion = st;
00178 }
00179
00180 void replay::set_skip(bool skip)
00181 {
00182 skip_ = skip;
00183 }
00184
00185 bool replay::is_skipping() const
00186 {
00187 return skip_;
00188 }
00189
00190 void replay::save_game(const std::string& label, const config& snapshot,
00191 const config& starting_pos, bool include_replay)
00192 {
00193 log_scope("replay::save_game");
00194 saveInfo_.snapshot = snapshot;
00195 saveInfo_.starting_pos = starting_pos;
00196
00197 if(include_replay) {
00198 saveInfo_.replay_data = cfg_;
00199 } else {
00200 saveInfo_.replay_data = config();
00201 }
00202
00203 saveInfo_.label = label;
00204
00205 std::string filename = label;
00206 if(preferences::compress_saves()) {
00207 filename += ".gz";
00208 }
00209
00210 scoped_ostream os(open_save_game(filename));
00211 config_writer out(*os, preferences::compress_saves(), PACKAGE);
00212 ::write_game(out, saveInfo_);
00213 finish_save_game(out, saveInfo_, saveInfo_.label);
00214
00215 saveInfo_.replay_data = config();
00216 saveInfo_.snapshot = config();
00217 }
00218
00219 void replay::add_unit_checksum(const gamemap::location& loc,config* const cfg)
00220 {
00221 if(! game_config::mp_debug) {
00222 return;
00223 }
00224 assert(unit_map_ref);
00225 config& cc = cfg->add_child("checksum");
00226 loc.write(cc);
00227 unit_map::const_iterator u = unit_map_ref->find(loc);
00228 assert(u != unit_map_ref->end());
00229 cc["value"] = get_checksum(u->second);
00230 }
00231
00232 void replay::add_start()
00233 {
00234 config* const cmd = add_command(true);
00235 cmd->add_child("start");
00236 }
00237
00238 void replay::add_recruit(int value, const gamemap::location& loc)
00239 {
00240 config* const cmd = add_command();
00241
00242 config val;
00243
00244 char buf[100];
00245 snprintf(buf,sizeof(buf),"%d",value);
00246 val["value"] = buf;
00247
00248 loc.write(val);
00249
00250 cmd->add_child("recruit",val);
00251 }
00252
00253 void replay::add_recall(int value, const gamemap::location& loc)
00254 {
00255 config* const cmd = add_command();
00256
00257 config val;
00258
00259 char buf[100];
00260 snprintf(buf,sizeof(buf),"%d",value);
00261 val["value"] = buf;
00262
00263 loc.write(val);
00264
00265 cmd->add_child("recall",val);
00266 }
00267
00268 void replay::add_disband(int value)
00269 {
00270 config* const cmd = add_command();
00271
00272 config val;
00273
00274 char buf[100];
00275 snprintf(buf,sizeof(buf),"%d",value);
00276 val["value"] = buf;
00277
00278 cmd->add_child("disband",val);
00279 }
00280
00281 void replay::add_countdown_update(int value, int team)
00282 {
00283 config* const cmd = add_command();
00284 config val;
00285
00286 val["value"] = lexical_cast_default<std::string>(value);
00287 val["team"] = lexical_cast_default<std::string>(team);
00288
00289 cmd->add_child("countdown_update",val);
00290 }
00291
00292
00293 void replay::add_movement(const gamemap::location& a,const gamemap::location& b)
00294 {
00295 add_pos("move",a,b);
00296 }
00297
00298 void replay::add_attack(const gamemap::location& a, const gamemap::location& b, int att_weapon, int def_weapon)
00299 {
00300 add_pos("attack",a,b);
00301 char buf[100];
00302 snprintf(buf,sizeof(buf),"%d",att_weapon);
00303 current_->child("attack")->values["weapon"] = buf;
00304 snprintf(buf,sizeof(buf),"%d",def_weapon);
00305 current_->child("attack")->values["defender_weapon"] = buf;
00306 add_unit_checksum(a,current_);
00307 add_unit_checksum(b,current_);
00308 }
00309
00310 void replay::add_pos(const std::string& type,
00311 const gamemap::location& a, const gamemap::location& b)
00312 {
00313 config* const cmd = add_command();
00314
00315 config move, src, dst;
00316 a.write(src);
00317 b.write(dst);
00318
00319 move.add_child("source",src);
00320 move.add_child("destination",dst);
00321 cmd->add_child(type,move);
00322 }
00323
00324 void replay::add_value(const std::string& type, int value)
00325 {
00326 config* const cmd = add_command();
00327
00328 config val;
00329
00330 char buf[100];
00331 snprintf(buf,sizeof(buf),"%d",value);
00332 val["value"] = buf;
00333
00334 cmd->add_child(type,val);
00335 }
00336
00337 void replay::choose_option(int index)
00338 {
00339 add_value("choose",index);
00340 }
00341
00342 void replay::text_input(std::string input)
00343 {
00344 config* const cmd = add_command();
00345
00346 config val;
00347 val["text"] = input;
00348
00349 cmd->add_child("input",val);
00350 }
00351
00352 void replay::set_random_value(const std::string& choice)
00353 {
00354 config* const cmd = add_command();
00355 config val;
00356 val["value"] = choice;
00357 cmd->add_child("random_number",val);
00358 }
00359
00360 void replay::add_label(const terrain_label* label)
00361 {
00362 assert(label);
00363 config* const cmd = add_command(false);
00364
00365 (*cmd)["undo"] = "no";
00366
00367 config val;
00368
00369 label->write(val);
00370
00371 cmd->add_child("label",val);
00372 }
00373
00374 void replay::clear_labels(const std::string& team_name)
00375 {
00376 config* const cmd = add_command(false);
00377
00378 (*cmd)["undo"] = "no";
00379 config val;
00380 val["team_name"] = team_name;
00381 cmd->add_child("clear_labels",val);
00382 }
00383
00384 void replay::add_rename(const std::string& name, const gamemap::location& loc)
00385 {
00386 config* const cmd = add_command(false);
00387 (*cmd)["async"] = "yes";
00388 config val;
00389 loc.write(val);
00390 val["name"] = name;
00391 cmd->add_child("rename", val);
00392 }
00393
00394 void replay::end_turn()
00395 {
00396 config* const cmd = add_command();
00397 cmd->add_child("end_turn");
00398 }
00399
00400 void replay::add_event(const std::string& name, const gamemap::location& loc)
00401 {
00402 config* const cmd = add_command();
00403 config& ev = cmd->add_child("fire_event");
00404 ev["raise"] = name;
00405 if(loc.valid()) {
00406 config& source = ev.add_child("source");
00407 loc.write(source);
00408 }
00409 (*cmd)["undo"] = "no";
00410 }
00411
00412 void replay::add_checksum_check(const gamemap::location& loc)
00413 {
00414 if(! game_config::mp_debug) {
00415 return;
00416 }
00417 config* const cmd = add_command();
00418 add_unit_checksum(loc,cmd);
00419 }
00420
00421 void replay::add_advancement(const gamemap::location& loc)
00422 {
00423 config* const cmd = add_command(false);
00424
00425 config val;
00426 (*cmd)["undo"] = "no";
00427 loc.write(val);
00428 cmd->add_child("advance_unit",val);
00429 }
00430
00431 void replay::add_chat_message_location()
00432 {
00433 message_locations.push_back(pos_-1);
00434 }
00435
00436 void replay::speak(const config& cfg)
00437 {
00438 config* const cmd = add_command(false);
00439 if(cmd != NULL) {
00440 cmd->add_child("speak",cfg);
00441 (*cmd)["undo"] = "no";
00442 add_chat_message_location();
00443 }
00444 }
00445
00446 void replay::add_chat_log_entry(const config* speak, std::stringstream& str, const std::string& team) const
00447 {
00448 if (!speak)
00449 {
00450 return;
00451 }
00452 const config& cfg = *speak;
00453 const std::string& team_name = cfg["team_name"];
00454 if(team_name == "" || team_name == team) {
00455 if(team_name == "") {
00456 str << "<" << cfg["id"] << "> ";
00457 } else {
00458 str << "*" << cfg["id"] << "* ";
00459 }
00460 str << cfg["message"] << "\n";
00461 }
00462
00463 }
00464
00465 void replay::remove_command(int index)
00466 {
00467 cfg_.remove_child("command", index);
00468 std::vector<int>::reverse_iterator loc_it;
00469 for (loc_it = message_locations.rbegin(); loc_it != message_locations.rend() && index < *loc_it;++loc_it)
00470 {
00471 --(*loc_it);
00472 }
00473 }
00474
00475
00476 std::stringstream message_log;
00477
00478
00479 std::string replay::build_chat_log(const std::string& team)
00480 {
00481 const config::child_list& cmd = commands();
00482 std::vector<int>::iterator loc_it;
00483 int last_location = 0;
00484 for (loc_it = message_locations.begin(); loc_it != message_locations.end(); ++loc_it)
00485 {
00486 last_location = *loc_it;
00487 const config* speak = cmd[last_location]->child("speak");
00488 add_chat_log_entry(speak,message_log,team);
00489
00490 }
00491 message_locations.clear();
00492
00493 #if 0
00494 for(config::child_list::const_iterator i = cmd.begin() + (last_location + 1); i != cmd.end(); ++i) {
00495 ++last_location;
00496 const config* speak = (**i).child("speak");
00497 if(speak != NULL) {
00498 message_locations.push_back(last_location);
00499 add_chat_log_entry(speak,str,team);
00500 }
00501 }
00502 #endif
00503 return message_log.str();
00504 }
00505
00506 config replay::get_data_range(int cmd_start, int cmd_end, DATA_TYPE data_type)
00507 {
00508 config res;
00509
00510 const config::child_list& cmd = commands();
00511 while(cmd_start < cmd_end) {
00512 if ((data_type == ALL_DATA || (*cmd[cmd_start])["undo"] == "no")
00513 && (*cmd[cmd_start])["sent"] != "yes")
00514 {
00515 res.add_child("command",*cmd[cmd_start]);
00516
00517 if(data_type == NON_UNDO_DATA) {
00518 (*cmd[cmd_start])["sent"] = "yes";
00519 }
00520 }
00521
00522 ++cmd_start;
00523 }
00524
00525 return res;
00526 }
00527
00528 void replay::undo()
00529 {
00530 config::child_itors cmd = cfg_.child_range("command");
00531 std::vector<config::child_iterator> async_cmds;
00532
00533
00534
00535
00536
00537 while(cmd.first != cmd.second && ((**(cmd.second-1))["undo"] == "no"
00538 || (**(cmd.second-1))["async"] == "yes"
00539 || (**(cmd.second-1))["sent"] == "yes"))
00540 {
00541 if ((**(cmd.second-1))["async"] == "yes")
00542 async_cmds.push_back(cmd.second-1);
00543 --cmd.second;
00544 }
00545
00546 if(cmd.first != cmd.second) {
00547 config* child;
00548 config& cmd_second = (**(cmd.second-1));
00549 if ((child = cmd_second.child("move")) != NULL)
00550 {
00551
00552
00553 gamemap::location dst(*(child->child("destination")),
00554 game_events::get_state_of_game());
00555 gamemap::location src(*(child->child("source")),
00556 game_events::get_state_of_game());
00557 for (std::vector<config::child_iterator>::iterator async_cmd =
00558 async_cmds.begin(); async_cmd != async_cmds.end(); async_cmd++)
00559 {
00560 config* async_child;
00561 if ((async_child = (***async_cmd).child("rename")) != NULL)
00562 {
00563 gamemap::location aloc(*async_child, game_events::get_state_of_game());
00564 if (dst == aloc)
00565 {
00566 src.write(*async_child);
00567 }
00568 }
00569 }
00570 }
00571 else if ((child = cmd_second.child("recruit")) != NULL
00572 || (child = cmd_second.child("recall")) != NULL)
00573 {
00574
00575
00576 gamemap::location src(*child, game_events::get_state_of_game());
00577 for (std::vector<config::child_iterator>::iterator async_cmd =
00578 async_cmds.begin(); async_cmd != async_cmds.end(); async_cmd++)
00579 {
00580 config* async_child;
00581 if ((async_child = (***async_cmd).child("rename")) != NULL)
00582 {
00583 gamemap::location aloc(*async_child, game_events::get_state_of_game());
00584 if (src == aloc)
00585 {
00586 remove_command(*async_cmd - cmd.first);
00587 }
00588 }
00589 }
00590 }
00591 }
00592
00593 remove_command(cmd.second - cmd.first - 1);
00594 current_ = NULL;
00595 set_random(NULL);
00596 }
00597
00598 const config::child_list& replay::commands() const
00599 {
00600 return cfg_.get_children("command");
00601 }
00602
00603 int replay::ncommands()
00604 {
00605 return commands().size();
00606 }
00607
00608 config* replay::add_command(bool update_random_context)
00609 {
00610 pos_ = ncommands()+1;
00611 current_ = &cfg_.add_child("command");
00612 if(update_random_context)
00613 set_random(current_);
00614
00615 return current_;
00616 }
00617
00618 void replay::start_replay()
00619 {
00620 pos_ = 0;
00621 }
00622
00623 void replay::revert_action()
00624 {
00625 if (pos_ > 0)
00626 --pos_;
00627 }
00628
00629 config* replay::get_next_action()
00630 {
00631 if(pos_ >= commands().size())
00632 return NULL;
00633
00634 LOG_REPLAY << "up to replay action " << pos_ + 1 << "/" << commands().size() << "\n";
00635
00636 current_ = commands()[pos_];
00637 set_random(current_);
00638 ++pos_;
00639 return current_;
00640 }
00641
00642 void replay::pre_replay()
00643 {
00644 if ((rng::random() == NULL) && (commands().size() > 0)){
00645 if (at_end())
00646 {
00647 add_command(true);
00648 }
00649 else
00650 {
00651 set_random(commands()[pos_]);
00652 }
00653 }
00654 }
00655
00656 bool replay::at_end() const
00657 {
00658 return pos_ >= commands().size();
00659 }
00660
00661 void replay::set_to_end()
00662 {
00663 pos_ = commands().size();
00664 current_ = NULL;
00665 set_random(NULL);
00666 }
00667
00668 void replay::clear()
00669 {
00670 message_locations.clear();
00671 message_log.str(std::string());
00672 cfg_ = config();
00673 pos_ = 0;
00674 current_ = NULL;
00675 set_random(NULL);
00676 skip_ = 0;
00677 }
00678
00679 bool replay::empty()
00680 {
00681 return commands().empty();
00682 }
00683
00684 void replay::add_config(const config& cfg, MARK_SENT mark)
00685 {
00686 for(config::const_child_itors i = cfg.child_range("command"); i.first != i.second; ++i.first) {
00687 config& cfg = cfg_.add_child("command",**i.first);
00688 if (cfg.child("speak"))
00689 {
00690 pos_ = ncommands();
00691 add_chat_message_location();
00692 }
00693 if(mark == MARK_AS_SENT) {
00694 cfg["sent"] = "yes";
00695 }
00696 }
00697 }
00698
00699 namespace {
00700
00701 replay* replay_src = NULL;
00702
00703 struct replay_source_manager
00704 {
00705 replay_source_manager(replay* o) : old_(replay_src)
00706 {
00707 replay_src = o;
00708 }
00709
00710 ~replay_source_manager()
00711 {
00712 replay_src = old_;
00713 }
00714
00715 private:
00716 replay* const old_;
00717 };
00718
00719 }
00720
00721 replay& get_replay_source()
00722 {
00723 if(replay_src != NULL) {
00724 return *replay_src;
00725 } else {
00726 return recorder;
00727 }
00728 }
00729
00730 static void check_checksums(game_display& disp,const unit_map& units,const config& cfg)
00731 {
00732 if(! game_config::mp_debug) {
00733 return;
00734 }
00735 for(config::child_list::const_iterator ci = cfg.get_children("checksum").begin(); ci != cfg.get_children("checksum").end(); ++ci) {
00736 gamemap::location loc(**ci, game_events::get_state_of_game());
00737 unit_map::const_iterator u = units.find(loc);
00738 if(u == units.end()) {
00739 std::stringstream message;
00740 message << "non existant unit to checksum at " << loc.x+1 << "," << loc.y+1 << "!";
00741 disp.add_chat_message(time(NULL), "verification", 1, message.str(),
00742 game_display::MESSAGE_PRIVATE, false);
00743 continue;
00744 }
00745 if(get_checksum(u->second) != (**ci)["value"]) {
00746 std::stringstream message;
00747 message << "checksum mismatch at " << loc.x+1 << "," << loc.y+1 << "!";
00748 disp.add_chat_message(time(NULL), "verification", 1, message.str(),
00749 game_display::MESSAGE_PRIVATE, false);
00750 }
00751 }
00752 }
00753
00754 bool do_replay(game_display& disp, const gamemap& map,
00755 unit_map& units, std::vector<team>& teams, int team_num,
00756 const gamestatus& state, game_state& state_of_game, replay* obj)
00757 {
00758 log_scope("do replay");
00759
00760 const replay_source_manager replaymanager(obj);
00761
00762
00763
00764 if (!get_replay_source().is_skipping()){
00765 disp.recalculate_minimap();
00766 }
00767
00768 const set_random_generator generator_setter(&get_replay_source());
00769
00770 update_locker lock_update(disp.video(),get_replay_source().is_skipping());
00771 return do_replay_handle(disp, map, units, teams, team_num, state, state_of_game,
00772 std::string(""));
00773 }
00774
00775 bool do_replay_handle(game_display& disp, const gamemap& map,
00776 unit_map& units, std::vector<team>& teams, int team_num,
00777 const gamestatus& state, game_state& state_of_game,
00778 const std::string& do_untill)
00779 {
00780
00781 std::deque<gamemap::location> advancing_units;
00782
00783 end_level_exception* delayed_exception = 0;
00784
00785 team& current_team = teams[team_num-1];
00786
00787 for(;;) {
00788 config* const cfg = get_replay_source().get_next_action();
00789 config* child;
00790
00791
00792
00793
00794 bool fix_shroud = false;
00795 if (cfg)
00796 {
00797 DBG_REPLAY << "Replay data:\n" << *cfg << "\n";
00798 }
00799 else
00800 {
00801 DBG_REPLAY << "Repaly data at end\n";
00802 }
00803
00804
00805
00806 if(advancing_units.empty() == false) {
00807 if(cfg == NULL) {
00808 replay::throw_error("promotion expected, but none found\n");
00809 }
00810
00811
00812
00813
00814 if((child = cfg->child("choose")) != NULL) {
00815
00816 const int val = lexical_cast_default<int>((*child)["value"]);
00817
00818 dialogs::animate_unit_advancement(units,advancing_units.front(),disp,val);
00819
00820 advancing_units.pop_front();
00821
00822
00823
00824 if(advancing_units.empty()) {
00825 check_victory(units, teams, disp);
00826 }
00827
00828 continue;
00829 }
00830 }
00831
00832
00833
00834 if(cfg == NULL) {
00835
00836 THROW_END_LEVEL_DELETE(delayed_exception);
00837 return false;
00838 }
00839
00840
00841 if (!do_untill.empty()
00842 && cfg->child(do_untill) != NULL)
00843 {
00844 get_replay_source().revert_action();
00845 THROW_END_LEVEL_DELETE(delayed_exception);
00846 return false;
00847 }
00848
00849
00850 else if ( (cfg->all_children().size() == 0) || (cfg->child("start") != NULL) ){
00851
00852
00853 } else if((child = cfg->child("speak")) != NULL) {
00854 const std::string& team_name = (*child)["team_name"];
00855 if (team_name == "" || (!is_observer()
00856 && teams[disp.viewing_team()].team_name() == team_name)
00857 || (is_observer() && team_name == "observer"))
00858 {
00859 const std::string& speaker_name = (*child)["id"];
00860 const std::string& message = (*child)["message"];
00861
00862 bool is_whisper = (speaker_name.find("whisper: ") == 0);
00863 get_replay_source().add_chat_message_location();
00864 if (!get_replay_source().is_skipping() || is_whisper) {
00865 const int side = lexical_cast_default<int>((*child)["side"],0);
00866 disp.add_chat_message(time(NULL), speaker_name, side, message,
00867 (team_name == "" ? game_display::MESSAGE_PUBLIC
00868 : game_display::MESSAGE_PRIVATE),
00869 preferences::message_bell());
00870 }
00871 }
00872 } else if((child = cfg->child("label")) != NULL) {
00873
00874 terrain_label label(disp.labels(),*child, game_events::get_state_of_game());
00875
00876 disp.labels().set_label(label.location(),
00877 label.text(),
00878 label.team_name(),
00879 label.colour());
00880
00881 } else if((child = cfg->child("clear_labels")) != NULL) {
00882
00883 disp.labels().clear(std::string((*child)["team_name"]));
00884 }
00885
00886 else if((child = cfg->child("rename")) != NULL) {
00887 const gamemap::location loc(*child, game_events::get_state_of_game());
00888 const std::string& name = (*child)["name"];
00889
00890 unit_map::iterator u = units.find(loc);
00891 if(u != units.end()) {
00892 if(u->second.unrenamable()) {
00893 std::stringstream errbuf;
00894 errbuf << "renaming unrenamable unit " << u->second.id() << "\n";
00895 replay::throw_error(errbuf.str());
00896 }
00897 u->second.rename(name);
00898 } else {
00899
00900
00901
00902
00903 WRN_REPLAY << "attempt to rename unit at location: "
00904 << loc << ", where none exists (anymore).\n";
00905 }
00906 }
00907
00908
00909 else if(cfg->child("end_turn") != NULL) {
00910 child = cfg->child("verify");
00911 if(child != NULL) {
00912 verify_units(*child);
00913 }
00914
00915 THROW_END_LEVEL_DELETE(delayed_exception);
00916 return true;
00917 }
00918
00919 else if((child = cfg->child("recruit")) != NULL) {
00920 const std::string& recruit_num = (*child)["value"];
00921 const int val = lexical_cast_default<int>(recruit_num);
00922
00923 gamemap::location loc(*child, game_events::get_state_of_game());
00924
00925 const std::set<std::string>& recruits = current_team.recruits();
00926
00927 if(val < 0 || static_cast<size_t>(val) >= recruits.size()) {
00928 std::stringstream errbuf;
00929 errbuf << "recruitment index is illegal: " << val
00930 << " while this side only has " << recruits.size()
00931 << " units available for recruitment\n";
00932 replay::throw_error(errbuf.str());
00933 }
00934
00935 std::set<std::string>::const_iterator itor = recruits.begin();
00936 std::advance(itor,val);
00937 const std::map<std::string,unit_type>::const_iterator u_type = unit_type_data::types().find(*itor);
00938 if(u_type == unit_type_data::types().end()) {
00939 std::stringstream errbuf;
00940 errbuf << "recruiting illegal unit: '" << *itor << "'\n";
00941 replay::throw_error(errbuf.str());
00942 }
00943
00944 unit new_unit(&units,&map,&state,&teams,&(u_type->second),team_num,true, false);
00945 const std::string& res = recruit_unit(map,team_num,units,new_unit,loc,false);
00946 if(!res.empty()) {
00947 std::stringstream errbuf;
00948 errbuf << "cannot recruit unit: " << res << "\n";
00949 replay::throw_error(errbuf.str());
00950 }
00951
00952 if(u_type->second.cost() > current_team.gold()) {
00953 std::stringstream errbuf;
00954 errbuf << "unit '" << u_type->second.id() << "' is too expensive to recruit: "
00955 << u_type->second.cost() << "/" << current_team.gold() << "\n";
00956 replay::throw_error(errbuf.str());
00957 }
00958 LOG_REPLAY << "recruit: team=" << team_num << " '" << u_type->second.id() << "' at (" << loc
00959 << ") cost=" << u_type->second.cost() << " from gold=" << current_team.gold() << ' ';
00960
00961
00962 statistics::recruit_unit(new_unit);
00963
00964 current_team.spend_gold(u_type->second.cost());
00965 LOG_REPLAY << "-> " << (current_team.gold()) << "\n";
00966 fix_shroud = !get_replay_source().is_skipping();
00967 check_checksums(disp,units,*cfg);
00968 }
00969
00970 else if((child = cfg->child("recall")) != NULL) {
00971 player_info* player = state_of_game.get_player(current_team.save_id());
00972 if(player == NULL) {
00973 replay::throw_error("illegal recall\n");
00974 }
00975
00976 sort_units(player->available_units);
00977
00978 const std::string& recall_num = (*child)["value"];
00979 const int val = lexical_cast_default<int>(recall_num);
00980
00981 gamemap::location loc(*child, game_events::get_state_of_game());
00982
00983 if(val >= 0 && val < int(player->available_units.size())) {
00984 statistics::recall_unit(player->available_units[val]);
00985 player->available_units[val].set_game_context(&units,&map,&state,&teams);
00986 recruit_unit(map,team_num,units,player->available_units[val],loc,true);
00987 player->available_units.erase(player->available_units.begin()+val);
00988 current_team.spend_gold(game_config::recall_cost);
00989 } else {
00990 replay::throw_error("illegal recall\n");
00991 }
00992 fix_shroud = !get_replay_source().is_skipping();
00993 check_checksums(disp,units,*cfg);
00994 }
00995
00996 else if((child = cfg->child("disband")) != NULL) {
00997 player_info* const player = state_of_game.get_player(current_team.save_id());
00998 if(player == NULL) {
00999 replay::throw_error("illegal disband\n");
01000 }
01001
01002 sort_units(player->available_units);
01003 const std::string& unit_num = (*child)["value"];
01004 const int val = lexical_cast_default<int>(unit_num);
01005
01006 if(val >= 0 && val < int(player->available_units.size())) {
01007 player->available_units.erase(player->available_units.begin()+val);
01008 } else {
01009 replay::throw_error("illegal disband\n");
01010 }
01011 }
01012 else if((child = cfg->child("countdown_update")) != NULL) {
01013 const std::string& num = (*child)["value"];
01014 const int val = lexical_cast_default<int>(num);
01015 const std::string& tnum = (*child)["team"];
01016 const int tval = lexical_cast_default<int>(tnum,-1);
01017 if ( (tval<0) || (static_cast<size_t>(tval) > teams.size()) ) {
01018 std::stringstream errbuf;
01019 errbuf << "Illegal countdown update \n"
01020 << "Received update for :" << tval << " Current user :"
01021 << team_num << "\n" << " Updated value :" << val;
01022
01023 replay::throw_error(errbuf.str());
01024 } else {
01025 teams[tval-1].set_countdown_time(val);
01026 }
01027 }
01028 else if((child = cfg->child("move")) != NULL) {
01029
01030 const config* const destination = child->child("destination");
01031 const config* const source = child->child("source");
01032
01033 if(destination == NULL || source == NULL) {
01034 replay::throw_error("no destination/source found in movement\n");
01035 }
01036
01037 const gamemap::location src(*source, game_events::get_state_of_game());
01038 const gamemap::location dst(*destination, game_events::get_state_of_game());
01039
01040 unit_map::iterator u = units.find(dst);
01041 if(u != units.end()) {
01042 std::stringstream errbuf;
01043 errbuf << "destination already occupied: "
01044 << dst << '\n';
01045 replay::throw_error(errbuf.str());
01046 }
01047 u = units.find(src);
01048 if(u == units.end()) {
01049 std::stringstream errbuf;
01050 errbuf << "unfound location for source of movement: "
01051 << src << " -> " << dst << '\n';
01052 replay::throw_error(errbuf.str());
01053 }
01054
01055 const bool teleport = u->second.get_ability_bool("teleport",u->first);
01056
01057 paths paths_list(map,units,src,teams,false,teleport,current_team);
01058
01059 std::map<gamemap::location,paths::route>::iterator rt = paths_list.routes.find(dst);
01060 if(rt == paths_list.routes.end()) {
01061
01062 std::stringstream errbuf;
01063 for(rt = paths_list.routes.begin(); rt != paths_list.routes.end(); ++rt) {
01064 errbuf << "can get to: " << rt->first << '\n';
01065 }
01066
01067 errbuf << "src cannot get to dst: " << u->second.movement_left() << ' '
01068 << paths_list.routes.size() << ' ' << src << " -> " << dst << '\n';
01069 replay::throw_error(errbuf.str());
01070 }
01071
01072 rt->second.steps.push_back(dst);
01073
01074 if(!get_replay_source().is_skipping()) {
01075 unit_display::move_unit(rt->second.steps,u->second,teams);
01076 }
01077 else{
01078
01079 u->second.set_goto(*(rt->second.steps.end() - 1));
01080 }
01081 u->second.set_movement(rt->second.move_left);
01082
01083 std::pair<gamemap::location,unit> *up = units.extract(u->first);
01084 up->first = dst;
01085 units.add(up);
01086 unit::clear_status_caches();
01087 if (up->first == up->second.get_goto())
01088 {
01089
01090 up->second.set_goto(gamemap::location());
01091 }
01092 up->second.set_standing(up->first);
01093 u = units.find(dst);
01094 check_checksums(disp,units,*cfg);
01095
01096 int u_team = u->second.side()-1;
01097 if(map.is_village(dst)) {
01098 const int orig_owner = village_owner(dst,teams) + 1;
01099 if(orig_owner != team_num) {
01100 u->second.set_movement(0);
01101 get_village(dst,disp,teams,team_num-1,units);
01102 }
01103 }
01104
01105 if(!get_replay_source().is_skipping()) {
01106 disp.invalidate(dst);
01107 disp.draw();
01108 }
01109
01110 game_events::fire("moveto",dst);
01111
01112 if(team_num != 1 && (teams.front().uses_shroud() || teams.front().uses_fog()) && !teams.front().fogged(dst)) {
01113 game_events::fire("sighted",dst);
01114 }
01115 for(std::vector<team>::iterator my_team = teams.begin() ; my_team != teams.end() ; my_team++) {
01116 if((my_team->uses_shroud() || my_team->uses_fog()) && !my_team->fogged(dst)) {
01117 my_team->see(u_team);
01118 }
01119 }
01120
01121 fix_shroud = !get_replay_source().is_skipping();
01122
01123
01124
01125 }
01126
01127 else if((child = cfg->child("attack")) != NULL) {
01128 const config* const destination = child->child("destination");
01129 const config* const source = child->child("source");
01130 check_checksums(disp,units,*cfg);
01131
01132 if(destination == NULL || source == NULL) {
01133 replay::throw_error("no destination/source found in attack\n");
01134 }
01135
01136
01137
01138 const gamemap::location src(*source, game_events::get_state_of_game());
01139 const gamemap::location dst(*destination, game_events::get_state_of_game());
01140
01141 const std::string& weapon = (*child)["weapon"];
01142 const int weapon_num = lexical_cast_default<int>(weapon);
01143
01144 const std::string& def_weapon = (*child)["defender_weapon"];
01145 int def_weapon_num = -1;
01146 if (def_weapon.empty()) {
01147
01148 ERR_REPLAY << "Old data, having to guess weapon\n";
01149 } else {
01150 def_weapon_num = lexical_cast_default<int>(def_weapon);
01151 }
01152
01153 unit_map::iterator u = units.find(src);
01154 if(u == units.end()) {
01155 replay::throw_error("unfound location for source of attack\n");
01156 }
01157
01158 if(size_t(weapon_num) >= u->second.attacks().size()) {
01159 replay::throw_error("illegal weapon type in attack\n");
01160 }
01161
01162 unit_map::const_iterator tgt = units.find(dst);
01163
01164 if(tgt == units.end()) {
01165 std::stringstream errbuf;
01166 errbuf << "unfound defender for attack: " << src << " -> " << dst << '\n';
01167 replay::throw_error(errbuf.str());
01168 }
01169
01170 if(def_weapon_num >=
01171 static_cast<int>(tgt->second.attacks().size())) {
01172
01173 replay::throw_error("illegal defender weapon type in attack\n");
01174 }
01175
01176 DBG_REPLAY << "Attacker XP (before attack): " << u->second.experience() << "\n";;
01177
01178 DELAY_END_LEVEL(delayed_exception, attack(disp, map, teams, src, dst, weapon_num, def_weapon_num, units, state, !get_replay_source().is_skipping()));
01179
01180 DBG_REPLAY << "Attacker XP (after attack): " << u->second.experience() << "\n";;
01181
01182 u = units.find(src);
01183 tgt = units.find(dst);
01184
01185 if(u != units.end() && u->second.advances()) {
01186 advancing_units.push_back(u->first);
01187 }
01188
01189 DBG_REPLAY << "advancing_units.size: " << advancing_units.size() << "\n";
01190 if(tgt != units.end() && tgt->second.advances()) {
01191 advancing_units.push_back(tgt->first);
01192 }
01193
01194
01195
01196 if(advancing_units.empty()) {
01197 check_victory(units, teams, disp);
01198 }
01199 fix_shroud = !get_replay_source().is_skipping();
01200 } else if((child = cfg->child("fire_event")) != NULL) {
01201 for(config::child_list::const_iterator v = child->get_children("set_variable").begin(); v != child->get_children("set_variable").end(); ++v) {
01202 state_of_game.set_variable((**v)["name"],(**v)["value"]);
01203 }
01204 const std::string event = (*child)["raise"];
01205
01206
01207 if ((event != "side turn") && (event != "turn 1") && (event != "new turn") && (event != "turn refresh")){
01208 const config* const source = child->child("source");
01209 if(source != NULL) {
01210 game_events::fire(event, gamemap::location(*source, game_events::get_state_of_game()));
01211 } else {
01212 game_events::fire(event);
01213 }
01214 }
01215
01216 } else if((child = cfg->child("advance_unit")) != NULL) {
01217 const gamemap::location loc(*child, game_events::get_state_of_game());
01218 advancing_units.push_back(loc);
01219
01220 } else {
01221 if(! cfg->child("checksum")) {
01222 replay::throw_error("unrecognized action:\n" + cfg->debug());
01223 } else {
01224 check_checksums(disp,units,*cfg);
01225 }
01226 }
01227
01228
01229
01230 if(fix_shroud && clear_shroud(disp,map,units,teams,team_num-1) && !recorder.is_skipping()) {
01231 disp.recalculate_minimap();
01232 disp.invalidate_game_status();
01233 disp.invalidate_all();
01234 disp.draw();
01235 }
01236
01237 child = cfg->child("verify");
01238 if(child != NULL) {
01239 verify_units(*child);
01240 }
01241 }
01242
01243 return false;
01244 }
01245
01246 replay_network_sender::replay_network_sender(replay& obj) : obj_(obj), upto_(obj_.ncommands())
01247 {
01248 }
01249
01250 replay_network_sender::~replay_network_sender()
01251 {
01252 commit_and_sync();
01253 }
01254
01255 void replay_network_sender::sync_non_undoable()
01256 {
01257 if(network::nconnections() > 0) {
01258 config cfg;
01259 const config& data = cfg.add_child("turn",obj_.get_data_range(upto_,obj_.ncommands(),replay::NON_UNDO_DATA));
01260 if(data.empty() == false) {
01261 network::send_data(cfg, 0, true);
01262 }
01263 }
01264 }
01265
01266 void replay_network_sender::commit_and_sync()
01267 {
01268 if(network::nconnections() > 0) {
01269 config cfg;
01270 const config& data = cfg.add_child("turn",obj_.get_data_range(upto_,obj_.ncommands()));
01271 if(data.empty() == false) {
01272 network::send_data(cfg, 0, true);
01273 }
01274
01275 upto_ = obj_.ncommands();
01276 }
01277 }