00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 #include "playturn.hpp"
00016
00017 #include "config.hpp"
00018 #include "construct_dialog.hpp"
00019 #include "game_display.hpp"
00020 #include "game_config.hpp"
00021 #include "game_preferences.hpp"
00022 #include "gamestatus.hpp"
00023 #include "gettext.hpp"
00024 #include "log.hpp"
00025 #include "menu_events.hpp"
00026 #include "replay.hpp"
00027 #include "sound.hpp"
00028 #include "team.hpp"
00029 #include "unit.hpp"
00030
00031 turn_info::turn_info(game_state& state_of_game,
00032 const gamestatus& status, game_display& gui, gamemap& map,
00033 std::vector<team>& teams, unsigned int team_num, unit_map& units,
00034 replay_network_sender& replay_sender, undo_list& undo_stack)
00035 : state_of_game_(state_of_game), status_(status),
00036 gui_(gui), map_(map), teams_(teams), team_num_(team_num),
00037 units_(units), undo_stack_(undo_stack),
00038 replay_sender_(replay_sender), replay_error_("network_replay_error"),
00039 host_transfer_("host_transfer")
00040 {}
00041
00042 turn_info::~turn_info(){
00043 undo_stack_.clear();
00044 }
00045
00046 void turn_info::sync_network()
00047 {
00048 if(network::nconnections() > 0) {
00049
00050
00051
00052
00053 config cfg;
00054 while(network::connection res = network::receive_data(cfg)) {
00055 std::deque<config> backlog;
00056 process_network_data(cfg,res,backlog,false);
00057 cfg.clear();
00058 }
00059
00060 send_data();
00061 }
00062 }
00063
00064 void turn_info::send_data()
00065 {
00066 if(undo_stack_.empty()) {
00067 replay_sender_.commit_and_sync();
00068 } else {
00069 replay_sender_.sync_non_undoable();
00070 }
00071 }
00072
00073 turn_info::PROCESS_DATA_RESULT turn_info::process_network_data(const config& cfg,
00074 network::connection from, std::deque<config>& backlog, bool skip_replay)
00075 {
00076 if (cfg.child("message")) {
00077 const config& cmessage = *cfg.child("message");
00078 const int side = lexical_cast_default<int>(cmessage["side"],0);
00079 gui_.add_chat_message(time(NULL), cmessage["sender"], side,
00080 cmessage["message"], game_display::MESSAGE_PUBLIC,
00081 preferences::message_bell());
00082 }
00083 if (cfg.child("whisper") != NULL ) {
00084 const config& cwhisper = *cfg.child("whisper");
00085 gui_.add_chat_message(time(NULL), "whisper: " + cwhisper["sender"], 0,
00086 cwhisper["message"], game_display::MESSAGE_PRIVATE,
00087 preferences::message_bell());
00088 }
00089 if(cfg.child("observer") != NULL) {
00090 const config::child_list& observers = cfg.get_children("observer");
00091 for(config::child_list::const_iterator ob = observers.begin(); ob != observers.end(); ++ob) {
00092 gui_.add_observer((**ob)["name"]);
00093 }
00094 }
00095
00096 if(cfg.child("observer_quit") != NULL) {
00097 const config::child_list& observers = cfg.get_children("observer_quit");
00098 for(config::child_list::const_iterator ob = observers.begin(); ob != observers.end(); ++ob) {
00099 gui_.remove_observer((**ob)["name"]);
00100 }
00101 }
00102
00103 if(cfg.child("leave_game") != NULL) {
00104 throw network::error("");
00105 }
00106
00107 bool turn_end = false;
00108
00109 const config::child_list& turns = cfg.get_children("turn");
00110 if(turns.empty() == false && from != network::null_connection) {
00111
00112 network::send_data_all_except(cfg, from, true);
00113 }
00114
00115 for(config::child_list::const_iterator t = turns.begin(); t != turns.end(); ++t) {
00116
00117 if(turn_end == false) {
00118
00119 replay replay_obj(**t);
00120 replay_obj.set_skip(skip_replay);
00121 replay_obj.start_replay();
00122
00123 try{
00124 turn_end = do_replay(gui_, map_, units_, teams_,
00125 team_num_, status_, state_of_game_, &replay_obj);
00126 }
00127 catch (replay::error& e){
00128
00129 config cfg;
00130 config& info = cfg.add_child("info");
00131 info["type"] = "termination";
00132 info["condition"] = "out of sync";
00133 network::send_data(cfg, 0, true);
00134
00135 replay::last_replay_error = e.message;
00136 replay_error_.notify_observers();
00137 }
00138
00139 recorder.add_config(**t,replay::MARK_AS_SENT);
00140 } else {
00141
00142
00143
00144 backlog.push_back(config());
00145 backlog.back().add_child("turn",**t);
00146 }
00147 }
00148
00149 if(const config* change= cfg.child("change_controller")) {
00150
00151 const int side = lexical_cast<int>((*change)["side"]);
00152 const size_t index = static_cast<size_t>(side-1);
00153
00154 const std::string& controller = (*change)["controller"];
00155 const std::string& player = (*change)["player"];
00156
00157 if(index < teams_.size()) {
00158 teams_[index].set_current_player(player);
00159 const unit_map::iterator leader = find_leader(units_, side);
00160 bool restart = gui_.get_playing_team() == index;
00161 if(leader != units_.end())
00162 leader->second.rename(player);
00163
00164
00165 if ( (controller == "human") && (!teams_[index].is_human()) ) {
00166 if (!teams_[gui_.get_playing_team()].is_human())
00167 {
00168 gui_.set_team(index);
00169 }
00170 teams_[index].make_human();
00171 } else if ( (controller == "network") && (!teams_[index].is_network()) ){
00172 teams_[index].make_network();
00173 } else if ( (controller == "ai") && (!teams_[index].is_ai()) ) {
00174 teams_[index].make_ai();
00175 }
00176 else
00177 {
00178 restart = false;
00179 }
00180
00181 return restart ? PROCESS_RESTART_TURN : PROCESS_CONTINUE;
00182 }
00183 }
00184
00185
00186 if(cfg["side_drop"] != "") {
00187 const std::string controller = cfg["controller"];
00188 const std::string side_str = cfg["side_drop"];
00189 const size_t side = atoi(side_str.c_str());
00190 const size_t side_index = side-1;
00191
00192 bool restart = side_index == gui_.get_playing_team();
00193
00194 if(side_index >= teams_.size()) {
00195 LOG_STREAM(err, network) << "unknown side " << side_index << " is dropping game\n";
00196 throw network::error("");
00197 }
00198
00199 const unit_map::iterator leader = find_leader(units_,side);
00200 const bool have_leader = (leader != units_.end());
00201
00202 if (controller == "ai"){
00203 teams_[side_index].make_ai();
00204 teams_[side_index].set_current_player("ai"+side_str);
00205 if(have_leader)
00206 leader->second.rename("ai"+side_str);
00207
00208
00209 return restart?PROCESS_RESTART_TURN:PROCESS_CONTINUE;
00210 }
00211
00212 int action = 0;
00213
00214 std::vector<std::string> observers;
00215 std::vector<team*> allies;
00216 std::vector<std::string> options;
00217
00218
00219
00220
00221 if(have_leader) {
00222 utils::string_map t_vars;
00223 options.push_back(_("Replace with AI"));
00224 options.push_back(_("Replace with local player"));
00225 options.push_back(_("Abort game"));
00226
00227
00228 for(std::set<std::string>::const_iterator ob = gui_.observers().begin(); ob != gui_.observers().end(); ++ob) {
00229 t_vars["player"] = *ob;
00230 options.push_back(vgettext("Replace with $player", t_vars));
00231 observers.push_back(*ob);
00232 }
00233
00234
00235 for (std::vector<team>::iterator team = teams_.begin(); team != teams_.end(); team++){
00236 if ( (!team->is_enemy(side)) && (!team->is_human()) && (!team->is_ai()) && (!team->is_empty())
00237 && (team->current_player() != teams_[side_index].current_player()) ){
00238
00239
00240
00241 t_vars["player"] = team->current_player();
00242 options.push_back(vgettext("Replace with $player", t_vars));
00243 allies.push_back(&(*team));
00244 }
00245 }
00246
00247 t_vars["player"] = teams_[side_index].current_player();
00248 const std::string msg = vgettext("$player has left the game. What do you want to do?", t_vars);
00249 gui::dialog dlg(gui_, "", msg, gui::OK_ONLY);
00250 dlg.set_menu(options);
00251 action = dlg.show();
00252 }
00253
00254
00255
00256
00257 switch(action) {
00258 case 0:
00259 teams_[side_index].make_ai();
00260 teams_[side_index].set_current_player("ai"+side_str);
00261 if(have_leader)
00262 leader->second.rename("ai"+side_str);
00263
00264
00265 return restart?PROCESS_RESTART_TURN:PROCESS_CONTINUE;
00266
00267
00268 case 1:
00269 teams_[side_index].make_human();
00270 teams_[side_index].set_current_player("human"+side_str);
00271 leader->second.rename("human"+side_str);
00272
00273
00274 return restart?PROCESS_RESTART_TURN:PROCESS_CONTINUE;
00275 case 2:
00276
00277
00278 throw end_level_exception(QUIT);
00279 default:
00280 if (action > 2) {
00281 const size_t index = static_cast<size_t>(action - 3);
00282 if (index < observers.size()) {
00283 change_side_controller(side_str, observers[index], false );
00284 } else if (index < options.size() - 1) {
00285 size_t i = index - observers.size();
00286 change_side_controller(side_str, allies[i]->save_id(), false );
00287 } else {
00288 teams_[side_index].make_ai();
00289 teams_[side_index].set_current_player("ai"+side_str);
00290 leader->second.rename("ai"+side_str);
00291 }
00292 return restart?PROCESS_RESTART_TURN:PROCESS_CONTINUE;
00293 }
00294 break;
00295 }
00296 throw network::error("");
00297 }
00298
00299
00300
00301 if (cfg.child("notify_next_scenario")) {
00302 gui::button* btn_end = gui_.find_button("button-endturn");
00303 if(btn_end) {
00304 btn_end->enable(true);
00305 }
00306 return PROCESS_END_LINGER;
00307 }
00308
00309
00310 if (const config* cfg_host_transfer = cfg.child("host_transfer")){
00311 if ( (*cfg_host_transfer)["value"] == "1"){
00312 host_transfer_.notify_observers();
00313 }
00314 }
00315
00316 return turn_end ? PROCESS_END_TURN : PROCESS_CONTINUE;
00317 }
00318
00319 void turn_info::change_side_controller(const std::string& side, const std::string& player, bool own_side)
00320 {
00321 config cfg;
00322 config& change = cfg.add_child("change_controller");
00323 change["side"] = side;
00324 change["player"] = player;
00325
00326 if(own_side) {
00327 change["own_side"] = "yes";
00328 }
00329
00330 network::send_data(cfg, 0, true);
00331 }
00332
00333 #if 0
00334 void turn_info::take_side(const std::string& side, const std::string& controller)
00335 {
00336 config cfg;
00337 cfg.values["side"] = side;
00338 cfg.values["controller"] = controller;
00339 cfg.values["name"] = controller+side;
00340 network::send_data(cfg, 0, true);
00341 }
00342 #endif