playturn.cpp

Go to the documentation of this file.
00001 /* $Id: playturn.cpp 25333 2008-03-30 13:49:03Z jhinrichs $ */
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 #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         //receive data first, and then send data. When we sent the end of
00051         //the AI's turn, we don't want there to be any chance where we
00052         //could get data back pertaining to the next turn.
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 /*&& is_observer()*/) {
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         //forward the data to other peers
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             //! @todo FIXME: Check what commands we execute when it's our turn!
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                 //notify remote hosts of out of sync error
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; //FIXME: some better way to pass this?
00136                 replay_error_.notify_observers();
00137             }
00138 
00139             recorder.add_config(**t,replay::MARK_AS_SENT);
00140         } else {
00141 
00142             //this turn has finished, so push the remaining moves
00143             //into the backlog
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         //don't use lexical_cast_default it's "safer" to end on error
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     //if a side has dropped out of the game.
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         //see if the side still has a leader alive. If they have
00219         //no leader, we assume they just want to be replaced by
00220         //the AI.
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             //get all observers in as options to transfer control
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             //get all allies in as options to transfer control
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                     //if this is an ally of the dropping side and it is not us (choose local player
00239                     //if you want that) and not ai or empty and if it is not the dropping side itself,
00240                     //get this team in as well
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         //make the player an AI, and redo this turn, in case
00255         //it was the current player's team who has just changed into
00256         //an AI.
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             //we don't have to test have_leader as action > 0 mean have_leader == true
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                 //The user pressed "end game". Don't throw a network error here or he will get
00277                 //thrown back to the title screen.
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 /*not our own side*/);
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 /*not our own side*/);
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     // The host has ended linger mode in a campaign -> enable the "End scenario" button
00300     // and tell we did get the notification.
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     //If this client becomes the new host, notify the play_controller object about it
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

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