multiplayer_connect.cpp

Go to the documentation of this file.
00001 /* $Id: multiplayer_connect.cpp 26687 2008-05-18 10:09:07Z noyga $ */
00002 /*
00003    Copyright (C) 2007 - 2008
00004    Part of the Battle for Wesnoth Project http://www.wesnoth.org
00005 
00006    This program is free software; you can redistribute it and/or modify
00007    it under the terms of the GNU General Public License version 2
00008    or at your option any later version.
00009    This program is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY.
00011 
00012    See the COPYING file for more details.
00013 */
00014 
00015 //! @file multiplayer_connect.cpp
00016 //! Prepare to join a multiplayer-game.
00017 
00018 #include "global.hpp"
00019 
00020 #include "ai.hpp"
00021 #include "dialogs.hpp"
00022 #include "font.hpp"
00023 #include "game_config.hpp"
00024 #include "game_events.hpp"
00025 #include "game_preferences.hpp"
00026 #include "gettext.hpp"
00027 #include "log.hpp"
00028 #include "multiplayer_connect.hpp"
00029 #include "statistics.hpp"
00030 #include "show_dialog.hpp"
00031 #include "serialization/string_utils.hpp"
00032 
00033 #include <cassert>
00034 
00035 #define LOG_NW LOG_STREAM(info, network)
00036 #define ERR_NW LOG_STREAM(err, network)
00037 #define LOG_CF LOG_STREAM(info, config)
00038 #define WRN_CF LOG_STREAM(warn, config)
00039 #define ERR_CF LOG_STREAM(err, config)
00040 
00041 namespace {
00042     const char* controller_names[] = {
00043         "network",
00044         "human",
00045         "ai",
00046         "null"
00047     };
00048 }
00049 
00050 namespace mp {
00051 
00052 connect::side::side(connect& parent, const config& cfg, int index) :
00053     parent_(&parent),
00054     cfg_(cfg),
00055     index_(index),
00056     id_(""), // Id is reset, and not imported from loading savegames
00057     save_id_(cfg_.get_attribute("save_id")),
00058     controller_(),
00059     faction_(lexical_cast_default<int>(cfg_.get_attribute("faction"), 0)),
00060     team_(0),
00061     colour_(index),
00062     gold_(lexical_cast_default<int>(cfg_.get_attribute("gold"), 100)),
00063     income_(lexical_cast_default<int>(cfg_.get_attribute("income"), 0)),
00064     leader_(),
00065     gender_(),
00066     ai_algorithm_(),
00067     player_number_(parent.video(), lexical_cast_default<std::string>(index+1, ""),
00068                    font::SIZE_LARGE, font::LOBBY_COLOUR),
00069     combo_controller_(parent.disp(), parent.player_types_),
00070     orig_controller_(parent.video(), cfg.get_attribute("id"), font::SIZE_SMALL),
00071     combo_ai_algorithm_(parent.disp(), std::vector<std::string>()),
00072     combo_faction_(parent.disp(), parent.player_factions_),
00073     combo_leader_(parent.disp(), std::vector<std::string>()),
00074     combo_gender_(parent.disp(), std::vector<std::string>()),
00075     combo_team_(parent.disp(), parent.player_teams_),
00076     combo_colour_(parent.disp(), parent.player_colours_),
00077     slider_gold_(parent.video()),
00078     slider_income_(parent.video()),
00079     label_gold_(parent.video(), "100", font::SIZE_SMALL, font::LOBBY_COLOUR),
00080     label_income_(parent.video(), _("Normal"), font::SIZE_SMALL, font::LOBBY_COLOUR),
00081     allow_player_(utils::string_bool(cfg_.get_attribute("allow_player"), true)),
00082     enabled_(!parent_->params_.saved_game), changed_(false),
00083     llm_(parent.era_sides_, enabled_ ? &combo_leader_ : NULL, enabled_ ? &combo_gender_ : NULL)
00084 {
00085     if(allow_player_ && enabled_) {
00086         controller_ = parent_->default_controller_;
00087     } else {
00088         size_t i = CNTR_NETWORK;
00089         // If player isn't allowed, network controller doesn't make sense
00090         if (!allow_player_)
00091         {
00092             cfg_["controller"] = controller_names[CNTR_COMPUTER];
00093             controller_ = static_cast<mp::controller>(CNTR_COMPUTER); // Set to ai
00094         }
00095         else
00096         {
00097             for(; i != CNTR_LAST; ++i) {
00098                 if(cfg_["controller"] == controller_names[i]) {
00099                     controller_ = static_cast<mp::controller>(i);
00100                     break;
00101                 }
00102             }
00103         }
00104     }
00105 
00106     slider_gold_.set_min(20);
00107     slider_gold_.set_max(800);
00108     slider_gold_.set_increment(25);
00109     slider_gold_.set_value(lexical_cast_default<int>(cfg_.get_attribute("gold"), 100));
00110     slider_gold_.set_measurements(80, 16);
00111 
00112     slider_income_.set_min(-2);
00113     slider_income_.set_max(18);
00114     slider_income_.set_increment(1);
00115     slider_income_.set_value(lexical_cast_default<int>(cfg_.get_attribute("income"), 0));
00116     slider_income_.set_measurements(50, 16);
00117 
00118     combo_faction_.enable(enabled_);
00119     combo_leader_.enable(enabled_);
00120     combo_gender_.enable(enabled_);
00121     combo_team_.enable(enabled_);
00122     combo_colour_.enable(enabled_);
00123     slider_gold_.hide(!enabled_);
00124     slider_income_.hide(!enabled_);
00125     label_gold_.hide(!enabled_);
00126     label_income_.hide(!enabled_);
00127 
00128     std::vector<std::string>::const_iterator itor =
00129             std::find(parent_->team_names_.begin(), parent_->team_names_.end(),
00130             cfg_.get_attribute("team_name"));
00131     if(itor == parent_->team_names_.end()) {
00132         assert(!parent_->team_names_.empty());
00133         team_ = 0;
00134     } else {
00135         team_ = itor - parent_->team_names_.begin();
00136     }
00137     if(!cfg_.get_attribute("colour").empty()) {
00138         colour_ = game_config::color_info(cfg_["colour"]).index() - 1;
00139     }
00140     config *ai = cfg_.child("ai");
00141     if (ai)
00142         ai_algorithm_ = lexical_cast_default<std::string>((*ai)["ai_algorithm"], "default");
00143     else
00144         ai_algorithm_ = "default";
00145     init_ai_algorithm_combo();
00146 
00147     // "Faction name" hack
00148     if (!enabled_) {
00149         faction_ = 0;
00150         std::vector<std::string> pseudo_factions;
00151         pseudo_factions.push_back(cfg_["name"]);
00152         combo_faction_.set_items(pseudo_factions);
00153         combo_faction_.set_selected(0);
00154 
00155         // Hack: if there is a unit which can recruit, use it as a leader.
00156         // Necessary to display leader information when loading saves.
00157         config::const_child_itors side_units = cfg.child_range("unit");
00158         std::string leader_type;
00159         for(;side_units.first != side_units.second; ++side_units.first) {
00160             if(utils::string_bool((**side_units.first)["canrecruit"], false)) {
00161                 leader_type = (**side_units.first)["type"];
00162                 gender_ = (**side_units.first)["gender"];
00163                 break;
00164             }
00165         }
00166         std::vector<std::string> leader_name_pseudolist;
00167         if(leader_type.empty()) {
00168             leader_name_pseudolist.push_back("-");
00169         } else {
00170             unit_type_data::unit_type_map::const_iterator leader_name =
00171                 unit_type_data::types().find(leader_type);
00172             if(leader_name == unit_type_data::types().end()) {
00173                 leader_name_pseudolist.push_back("-");
00174             } else {
00175                 if (gender_ == "female")
00176                     leader_name_pseudolist.push_back(leader_name->second.get_gender_unit_type(unit_race::FEMALE).type_name());
00177                 else if (gender_ == "male")
00178                     leader_name_pseudolist.push_back(leader_name->second.get_gender_unit_type(unit_race::MALE).type_name());
00179                 else
00180                     leader_name_pseudolist.push_back(leader_name->second.type_name());
00181             }
00182         }
00183         combo_leader_.set_items(leader_name_pseudolist);
00184         combo_leader_.set_selected(0);
00185         std::vector<std::string> gender_name_pseudolist;
00186 
00187         if (!gender_.empty()) {
00188             if (leader_type.empty()
00189                     || unit_type_data::types().find(leader_type)
00190                     == unit_type_data::types().end())
00191             {
00192                 gender_name_pseudolist.push_back("-");
00193             } else {
00194                 if (gender_ == "female")
00195                     gender_name_pseudolist.push_back( _("Female ♀") );
00196                 else if (gender_ == "male")
00197                     gender_name_pseudolist.push_back( _("Male ♂") );
00198                 else if (gender_ == "random")
00199                     gender_name_pseudolist.push_back( _("gender^Random") );
00200                 else gender_name_pseudolist.push_back("?");
00201             }
00202         } else {
00203             gender_name_pseudolist.push_back("-");
00204         }
00205         combo_gender_.set_items(gender_name_pseudolist);
00206         combo_gender_.set_selected(0);
00207     } else if(parent_->params_.use_map_settings) {
00208         // gold, income, team, and colour are only suggestions unless explicitly locked
00209         if(utils::string_bool(cfg_.get_attribute("gold_lock"), false)) {
00210             slider_gold_.enable(false);
00211             label_gold_.enable(false);
00212         }
00213         if(utils::string_bool(cfg_.get_attribute("income_lock"), false)) {
00214             slider_income_.enable(false);
00215             label_income_.enable(false);
00216         }
00217         if(utils::string_bool(cfg_.get_attribute("team_lock"), false)) {
00218             combo_team_.enable(false);
00219         }
00220         if(utils::string_bool(cfg_.get_attribute("colour_lock"), false)) {
00221             combo_colour_.enable(false);
00222         }
00223 
00224         // Set the leader and gender
00225         leader_ = cfg_.get_attribute("type");
00226         gender_ = cfg_.get_attribute("gender");
00227         if(!leader_.empty()) {
00228             combo_leader_.enable(false);
00229             combo_gender_.enable(false);
00230             llm_.set_leader_combo(NULL);
00231             llm_.set_gender_combo(NULL);
00232             std::vector<std::string> leader_name_pseudolist;
00233             unit_type_data::unit_type_map::const_iterator leader_name = unit_type_data::types().find(leader_);
00234             if(leader_name == unit_type_data::types().end()) {
00235                 leader_name_pseudolist.push_back("?");
00236             } else {
00237                 leader_name_pseudolist.push_back(leader_name->second.type_name());
00238             }
00239             combo_leader_.set_items(leader_name_pseudolist);
00240             combo_leader_.set_selected(0);
00241             std::vector<std::string> gender_name_pseudolist;
00242             if (!gender_.empty()) {
00243                 if(leader_name == unit_type_data::types().end()) {
00244                     gender_name_pseudolist.push_back("?");
00245                 } else {
00246                     if (gender_ == "female")
00247                         gender_name_pseudolist.push_back( _("Female ♀") );
00248                     else if (gender_ == "male")
00249                         gender_name_pseudolist.push_back( _("Male ♂") );
00250                     else if (gender_ == "random")
00251                         gender_name_pseudolist.push_back( _("gender^Random") );
00252                     else gender_name_pseudolist.push_back("?");
00253                 }
00254             } else gender_name_pseudolist.push_back("?");
00255             combo_gender_.set_items(gender_name_pseudolist);
00256             combo_gender_.set_selected(0);
00257         }
00258 
00259         // Try to pick a faction for the sake of appearance
00260         // and for filling in the blanks
00261         if(faction_ == 0) {
00262             std::vector<std::string> find;
00263             std::string search_field;
00264             if(!cfg_.get_attribute("faction").empty()) {
00265                 // Choose based on faction
00266                 find.push_back(cfg_["faction"]);
00267                 search_field = "id";
00268             } else if(utils::string_bool(cfg["faction_from_recruit"]) && !cfg_.get_attribute("recruit").empty()) {
00269                 // Choose based on recruit
00270                 find = utils::split(cfg_["recruit"]);
00271                 search_field = "recruit";
00272             } else if(!leader_.empty()) {
00273                 // Choose based on leader
00274                 find.push_back(leader_);
00275                 search_field = "leader";
00276             }
00277             // Pick the first faction with the greater amount of data matching the criteria
00278             int faction_index = 0;
00279             int best_score = 0;
00280             std::vector<config*>::const_iterator faction = parent.era_sides_.begin();
00281             while(faction != parent.era_sides_.end()) {
00282                 int faction_score = 0;
00283                 const config& side = (**faction);
00284                 std::vector<std::string> recruit;
00285                 recruit = utils::split(side[search_field]);
00286                 std::vector<std::string>::const_iterator search = find.begin();
00287                 while(search != find.end()) {
00288                     for(itor = recruit.begin(); itor != recruit.end(); ++itor) {
00289                         if(*itor == *search) {
00290                             faction_score++;
00291                             break;
00292                         }
00293                     }
00294                     ++search;
00295                 }
00296                 if(faction_score > best_score) {
00297                     best_score = faction_score;
00298                     faction_ = faction_index;
00299                 }
00300                 ++faction;
00301                 faction_index++;
00302             }
00303             if (faction_) {
00304                 llm_.update_leader_list(faction_);
00305                 llm_.update_gender_list(llm_.get_leader());
00306                 combo_faction_.enable(false);
00307             }
00308         } else {
00309             combo_faction_.enable(false);
00310         }
00311     }
00312 
00313     update_ui();
00314 }
00315 
00316 connect::side::side(const side& a) :
00317     parent_(a.parent_), cfg_(a.cfg_),
00318     index_(a.index_), id_(a.id_),  save_id_(a.save_id_),
00319     controller_(a.controller_),
00320     faction_(a.faction_), team_(a.team_), colour_(a.colour_),
00321     gold_(a.gold_), income_(a.income_), leader_(a.leader_), /* taken_(a.taken_), */
00322     gender_(a.gender_),
00323     ai_algorithm_(a.ai_algorithm_),
00324     player_number_(a.player_number_), combo_controller_(a.combo_controller_),
00325     orig_controller_(a.orig_controller_),
00326     combo_ai_algorithm_(a.combo_ai_algorithm_),
00327     combo_faction_(a.combo_faction_), combo_leader_(a.combo_leader_), combo_gender_(a.combo_gender_),
00328     combo_team_(a.combo_team_), combo_colour_(a.combo_colour_),
00329     slider_gold_(a.slider_gold_), slider_income_(a.slider_income_),
00330     label_gold_(a.label_gold_), label_income_(a.label_income_),
00331     allow_player_(a.allow_player_), enabled_(a.enabled_),
00332     changed_(a.changed_), llm_(a.llm_)
00333 {
00334     llm_.set_leader_combo((enabled_ && leader_.empty()) ? &combo_leader_ : NULL);
00335     llm_.set_gender_combo((enabled_ && leader_.empty()) ? &combo_gender_ : NULL);
00336     // FIXME: this is an ugly hack to force updating the gender list when the side
00337     // widget is initialized. Need an optimal way. -- shadowmaster
00338     llm_.update_gender_list(llm_.get_leader());
00339 }
00340 
00341 void connect::side::add_widgets_to_scrollpane(gui::scrollpane& pane, int pos)
00342 {
00343     pane.add_widget(&player_number_,     0, 5 + pos);
00344     pane.add_widget(&combo_controller_, 20, 5 + pos);
00345     pane.add_widget(&orig_controller_,  20 + (combo_controller_.width() - orig_controller_.width()) / 2,
00346                                         35 + pos + (combo_leader_.height() - orig_controller_.height()) / 2);
00347     pane.add_widget(&combo_ai_algorithm_, 20, 35 + pos);
00348     pane.add_widget(&combo_faction_, 135, 5 + pos);
00349     pane.add_widget(&combo_leader_,  135, 35 + pos);
00350     pane.add_widget(&combo_gender_,  250, 35 + pos);
00351     pane.add_widget(&combo_team_,    250, 5 + pos);
00352     pane.add_widget(&combo_colour_,  365, 5 + pos);
00353     pane.add_widget(&slider_gold_,   475, 5 + pos);
00354     pane.add_widget(&label_gold_,    475, 35 + pos);
00355     pane.add_widget(&slider_income_, 475 + slider_gold_.width(),  5 + pos);
00356     pane.add_widget(&label_income_,  475 + slider_gold_.width(), 35 + pos);
00357 }
00358 
00359 void connect::side::process_event()
00360 {
00361     if(combo_controller_.changed() && combo_controller_.selected() >= 0) {
00362         if (combo_controller_.selected() == CNTR_LAST) {
00363             update_controller_ui();
00364         } else if (combo_controller_.selected() < CNTR_LAST) {
00365             // If the current side corresponds to an existing user,
00366             // we must kick it!
00367 
00368             // Update controller first, or else kick will reset it.
00369             controller_ = mp::controller(combo_controller_.selected());
00370 
00371             // Don't kick an empty player or the game creator
00372             if(!id_.empty()) {
00373                 if (id_ != preferences::login()) {
00374                     parent_->kick_player(id_);
00375                 }
00376                 id_ = "";
00377             }
00378             changed_ = true;
00379         } else {
00380             size_t user = combo_controller_.selected() - CNTR_LAST - 1;
00381 
00382             // If the selected user already was attributed to
00383             // another side, find its side, and switch users.
00384             //! todo Let one player get several sides like it is
00385             //! already possible once the game started.
00386             const std::string new_id = parent_->users_[user].name;
00387             if (new_id != id_) {
00388                 int old_side = parent_->find_player_side(new_id);
00389                 if (old_side != -1) {
00390                     if (id_.empty()) {
00391                         parent_->sides_[old_side].set_controller(parent_->default_controller_);
00392                     } else {
00393                         parent_->sides_[old_side].set_id(id_);
00394                     }
00395                 }
00396                 id_ = new_id;
00397                 controller_ = parent_->users_[user].controller;
00398                 changed_ = true;
00399             }
00400         }
00401         update_ai_algorithm_combo();
00402     }
00403 
00404     if(!enabled_)
00405         return;
00406 
00407     if (combo_faction_.changed() && combo_faction_.selected() >= 0) {
00408         faction_ = combo_faction_.selected();
00409         llm_.update_leader_list(faction_);
00410         llm_.update_gender_list(llm_.get_leader());
00411         changed_ = true;
00412     }
00413     if (combo_ai_algorithm_.changed() && combo_ai_algorithm_.selected() >= 0) {
00414         ai_algorithm_ = parent_->ai_algorithms_[combo_ai_algorithm_.selected()];
00415         changed_ = true;
00416     }
00417     if (combo_leader_.changed() && combo_leader_.selected() >= 0) {
00418         llm_.update_gender_list(llm_.get_leader());
00419         changed_ = true;
00420     }
00421     if (combo_gender_.changed() && combo_gender_.selected() >= 0) {
00422         llm_.set_leader_combo(&combo_leader_);
00423         changed_ = true;
00424     }
00425     if (combo_team_.changed() && combo_team_.selected() >= 0) {
00426         team_ = combo_team_.selected();
00427         changed_ = true;
00428     }
00429     if (combo_colour_.changed() && combo_colour_.selected() >= 0) {
00430         colour_ = combo_colour_.selected();
00431         changed_ = true;
00432     }
00433     if (slider_gold_.value() != gold_) {
00434         gold_ = slider_gold_.value();
00435         label_gold_.set_text(lexical_cast_default<std::string>(gold_, "0"));
00436         changed_ = true;
00437     }
00438     if (slider_income_.value() != income_) {
00439 
00440         income_ = slider_income_.value();
00441         std::stringstream buf;
00442         if(income_ < 0) {
00443             buf << _("(") << income_ << _(")");
00444         } else if(income_ > 0) {
00445             buf << _("+") << income_;
00446         } else {
00447             buf << _("Normal");
00448         }
00449         label_income_.set_text(buf.str());
00450         changed_ = true;
00451     }
00452 }
00453 
00454 bool connect::side::changed()
00455 {
00456     bool res = changed_;
00457     changed_ = false;
00458     return res;
00459 }
00460 
00461 bool connect::side::available() const
00462 {
00463     return allow_player_ && controller_ == CNTR_NETWORK && id_.empty();
00464 }
00465 
00466 bool connect::side::allow_player() const
00467 {
00468     return allow_player_;
00469 }
00470 
00471 void connect::side::update_controller_ui()
00472 {
00473     if (id_.empty()) {
00474         combo_controller_.set_selected(controller_);
00475     } else {
00476         connected_user_list::iterator player = parent_->find_player(id_);
00477 
00478         if (player != parent_->users_.end()) {
00479             combo_controller_.set_selected(CNTR_LAST + 1 + (player - parent_->users_.begin()));
00480         } else {
00481             combo_controller_.set_selected(CNTR_NETWORK);
00482         }
00483     }
00484 
00485     update_ai_algorithm_combo();
00486 }
00487 
00488 void connect::side::hide_ai_algorithm_combo(bool invis)
00489 {
00490     if(!invis)
00491     {
00492         if(controller_ == CNTR_COMPUTER)
00493         {
00494             // Computer selected, show AI combo
00495             orig_controller_.hide(true);
00496             combo_ai_algorithm_.hide(false);
00497         } else {
00498             // Computer de-selected, hide AI combo
00499             combo_ai_algorithm_.hide(true);
00500             orig_controller_.hide(false);
00501         }
00502     } else {
00503         combo_ai_algorithm_.hide(true);
00504     }
00505 }
00506 
00507 void connect::side::init_ai_algorithm_combo()
00508 {
00509     assert(parent_->ai_algorithms_.empty() == false);
00510 
00511     int sel = 0;
00512     std::vector<std::string> ais = parent_->ai_algorithms_;
00513     for (unsigned int i = 0; i < ais.size(); i++) {
00514         if (ais[i] == ai_algorithm_) {
00515             sel = i;
00516         }
00517         if (ais[i] == "default") {
00518             ais[i] = _("Default AI");
00519         }
00520     }
00521     combo_ai_algorithm_.set_items(ais);
00522     combo_ai_algorithm_.set_selected(sel);
00523 }
00524 
00525 void connect::side::update_ui()
00526 {
00527     update_controller_ui();
00528 
00529     if (combo_faction_.selected() != faction_ && combo_faction_.selected() >= 0) {
00530         combo_faction_.set_selected(faction_);
00531     }
00532 
00533     combo_team_.set_selected(team_);
00534     combo_colour_.set_selected(colour_);
00535     slider_gold_.set_value(gold_);
00536     label_gold_.set_text(lexical_cast_default<std::string>(gold_, "0"));
00537     slider_income_.set_value(income_);
00538     std::stringstream buf;
00539     if(income_ < 0) {
00540         buf << _("(") << income_ << _(")");
00541     } else if(income_ > 0) {
00542         buf << _("+") << income_;
00543     } else {
00544         buf << _("Normal");
00545     }
00546     label_income_.set_text(buf.str());
00547 }
00548 
00549 config connect::side::get_config() const
00550 {
00551     config res(cfg_);
00552 
00553     // If the user is allowed to change type, faction, leader etc,
00554     // then import their new values in the config.
00555     if(enabled_ && !parent_->era_sides_.empty()) {
00556         // Merge the faction data to res
00557         res.append(*(parent_->era_sides_[faction_]));
00558     }
00559     if (cfg_.get_attribute("side").empty()
00560             || cfg_["side"] != lexical_cast<std::string>(index_ + 1))
00561     {
00562         res["side"] = lexical_cast<std::string>(index_ + 1);
00563     }
00564     res["controller"] = controller_names[controller_];
00565     res["id"] = id_;
00566     res["current_player"] = id_;
00567 
00568     if (id_.empty()) {
00569         char const *description;
00570         switch(controller_) {
00571         case CNTR_NETWORK:
00572             description = N_("(Vacant slot)");
00573             break;
00574         case CNTR_LOCAL:
00575             if(enabled_ && cfg_.get_attribute("save_id").empty()) {
00576                 res["save_id"] = "local" + res["side"].str();
00577             }
00578             description = N_("Anonymous local player");
00579             break;
00580         case CNTR_COMPUTER:
00581             if(enabled_ && cfg_.get_attribute("save_id").empty()) {
00582                 res["save_id"] = "ai" + res["side"].str();
00583             }
00584             {
00585                 config *ai = res.child("ai");
00586                 if (!ai) ai = &res.add_child("ai");
00587                 #ifdef HAVE_PYTHON
00588                 if (ai_algorithm_.substr(ai_algorithm_.length() - 3) == ".py") {
00589                     (*ai)["ai_algorithm"] = "python_ai";
00590                     (*ai)["python_script"] = ai_algorithm_;
00591                 }
00592                 else
00593                 #endif
00594                 {
00595                     if (ai_algorithm_ != "default")
00596                         (*ai)["ai_algorithm"] = ai_algorithm_;
00597                 }
00598             }
00599             description = N_("Computer player");
00600             break;
00601         case CNTR_EMPTY:
00602             description = N_("(Empty slot)");
00603             res["no_leader"] = "yes";
00604             break;
00605         default:
00606             assert(false);
00607             break;
00608         }
00609         res["user_description"] = t_string(description, "wesnoth");
00610     } else {
00611         if(enabled_ && cfg_.get_attribute("save_id").empty()) {
00612             res["save_id"] = id_;
00613         }
00614 
00615         res["user_description"] = id_;
00616     }
00617 
00618     if(enabled_) {
00619         if (leader_.empty()) {
00620             res["type"] = llm_.get_leader();
00621         } else {
00622             res["type"] = leader_;
00623         }
00624         if (gender_.empty()) {
00625             std::string dummy = llm_.get_gender();
00626             if (!dummy.empty() && dummy != "null" && dummy != "?")
00627                 res["gender"] = dummy;
00628         } else {
00629             // If no genders could be resolved, let the unit engine use
00630             // the default gender
00631             if (gender_ != "null")
00632                 res["gender"] = gender_;
00633         }
00634         // res["team"] = lexical_cast<std::string>(team_);
00635         res["team_name"] = parent_->team_names_[team_];
00636         res["user_team_name"] = parent_->user_team_names_[team_];
00637         res["allow_player"] = allow_player_ ? "yes" : "no";
00638         res["colour"] = lexical_cast<std::string>(colour_ + 1);
00639         res["gold"] = lexical_cast<std::string>(gold_);
00640         res["income"] = lexical_cast<std::string>(income_);
00641 
00642         if(!parent_->params_.use_map_settings || res["fog"].empty() || (res["fog"] != "yes" && res["fog"] != "no")) {
00643             res["fog"] = parent_->params_.fog_game ? "yes" : "no";
00644         }
00645 
00646         if(!parent_->params_.use_map_settings || res["shroud"].empty() || (res["shroud"] != "yes" && res["shroud"] != "no")) {
00647             res["shroud"] = parent_->params_.shroud_game ? "yes" : "no";
00648         }
00649 
00650 
00651         if(!parent_->params_.use_map_settings || res["mp_countdown"].empty() || (res["mp_countdown"] != "yes" && res["mp_countdown"] != "no")) {
00652             res["mp_countdown"] = parent_->params_.mp_countdown ? "yes" : "no";;
00653         }
00654 
00655         if(!parent_->params_.use_map_settings || res["mp_countdown_init_time"].empty()) {
00656             res["mp_countdown_init_time"] = lexical_cast<std::string>(parent_->params_.mp_countdown_init_time);
00657         }
00658         if(!parent_->params_.use_map_settings || res["mp_countdown_turn_bonus"].empty()) {
00659             res["mp_countdown_turn_bonus"] = lexical_cast<std::string>(parent_->params_.mp_countdown_turn_bonus);
00660         }
00661         if(!parent_->params_.use_map_settings || res["mp_countdown_reservoir_time"].empty()) {
00662             res["mp_countdown_reservoir_time"] = lexical_cast<std::string>(parent_->params_.mp_countdown_reservoir_time);
00663         }
00664         if(!parent_->params_.use_map_settings || res["mp_countdown_action_bonus"].empty()) {
00665             res["mp_countdown_action_bonus"] = lexical_cast<std::string>(parent_->params_.mp_countdown_action_bonus);
00666         }
00667 
00668         res["share_maps"] = parent_->params_.share_maps ? "yes" : "no";
00669         res["share_view"] =  parent_->params_.share_view ? "yes" : "no";
00670         if(!parent_->params_.use_map_settings || res["village_gold"].empty())
00671             res["village_gold"] = lexical_cast<std::string>(parent_->params_.village_gold);
00672 
00673         res["allow_changes"] = "yes";
00674     } else {
00675         res["allow_changes"] = "no";
00676     }
00677 
00678     if(parent_->params_.use_map_settings && enabled_) {
00679         config trimmed = cfg_;
00680         trimmed["side"] = "";
00681         trimmed["controller"] = "";
00682         trimmed["id"] = "";
00683         trimmed["team_name"] = "";
00684         trimmed["user_team_name"] = "";
00685         trimmed["colour"] = "";
00686         trimmed["gold"] = "";
00687         trimmed["income"] = "";
00688         trimmed["allow_changes"] = "";
00689 /*
00690         trimmed["allow_player"] = "";
00691         trimmed["colour_lock"] = "";
00692         trimmed["faction"] = "";
00693         trimmed["gender"] = "";
00694         trimmed["gold_lock"] = "";
00695         trimmed["income_lock"] = "";
00696         trimmed["recruit"] = "";
00697         trimmed["team_lock"] = "";
00698 */
00699         if(controller_ != CNTR_COMPUTER) {
00700             // Only override names for computer controlled players
00701             trimmed["user_description"] = "";
00702         }
00703         trimmed.prune();
00704         res.merge_with(trimmed);
00705     }
00706     return res;
00707 }
00708 
00709 void connect::side::set_controller(mp::controller controller)
00710 {
00711     controller_ = controller;
00712     id_ = "";
00713 
00714     update_ui();
00715 }
00716 
00717 mp::controller connect::side::get_controller() const
00718 {
00719     return controller_;
00720 }
00721 
00722 void connect::side::update_user_list()
00723 {
00724     bool name_present = false;
00725 
00726     std::vector<std::string> list = parent_->player_types_;
00727     list.push_back("----");
00728 
00729     connected_user_list::const_iterator itor;
00730     for (itor = parent_->users_.begin(); itor != parent_->users_.end();
00731             ++itor) {
00732         list.push_back(itor->name);
00733         if (itor->name == id_)
00734             name_present = true;
00735     }
00736 
00737     if (name_present == false) {
00738         id_ = "";
00739     }
00740 
00741     combo_controller_.set_items(list);
00742     update_controller_ui();
00743 }
00744 
00745 const std::string& connect::side::get_id() const
00746 {
00747     return id_;
00748 }
00749 
00750 void connect::side::set_id(const std::string& id)
00751 {
00752     connected_user_list::iterator i = parent_->find_player(id);
00753     if (i != parent_->users_.end()) {
00754         id_ = id;
00755         controller_ = i->controller;
00756     }
00757     update_ui();
00758 }
00759 
00760 const std::string& connect::side::get_save_id() const
00761 {
00762     return save_id_;
00763 }
00764 
00765 void connect::side::import_network_user(const config& data)
00766 {
00767     id_ = data["name"];
00768     controller_ = CNTR_NETWORK;
00769 
00770     if(enabled_ && !parent_->era_sides_.empty()) {
00771         if(combo_faction_.enabled()) {
00772             faction_ = lexical_cast_default<int>(data["faction"], 0);
00773             if(faction_ > int(parent_->era_sides_.size()))
00774                 faction_ = 0;
00775             llm_.update_leader_list(faction_);
00776             llm_.update_gender_list(llm_.get_leader());
00777         }
00778         if(combo_leader_.enabled()) {
00779             llm_.set_leader(data["leader"]);
00780             // FIXME: not optimal, but this hack is necessary to do after updating
00781             // the leader selection. Otherwise, gender gets always forced to "male".
00782             llm_.update_gender_list(llm_.get_leader());
00783         }
00784         if (combo_gender_.enabled()) {
00785             llm_.set_gender(data["gender"]);
00786         }
00787     }
00788 
00789     update_ui();
00790 }
00791 
00792 void connect::side::reset(mp::controller controller)
00793 {
00794     id_ = "";
00795     controller_ = controller;
00796 
00797     if(enabled_ && !parent_->era_sides_.empty()) {
00798         if(combo_faction_.enabled())
00799             faction_ = 0;
00800         if(combo_leader_.enabled())
00801             llm_.update_leader_list(0);
00802         if (combo_gender_.enabled())
00803             llm_.update_gender_list(llm_.get_leader());
00804     }
00805 
00806     update_ui();
00807 }
00808 
00809 void connect::side::resolve_random()
00810 {
00811     if(!enabled_ || parent_->era_sides_.empty())
00812         return;
00813 
00814     if(utils::string_bool(
00815             (*parent_->era_sides_[faction_]).get_attribute("random_faction"),
00816             false))
00817     {
00818         std::vector<std::string> faction_choices, faction_excepts;
00819         faction_choices = utils::split((*parent_->era_sides_[faction_]).get_attribute("choices"));
00820         if(faction_choices.size() == 1 && faction_choices.front() == "") {
00821             faction_choices.clear();
00822         }
00823         faction_excepts = utils::split((*parent_->era_sides_[faction_]).get_attribute("except"));
00824         if(faction_excepts.size() == 1 && faction_excepts.front() == "") {
00825             faction_excepts.clear();
00826         }
00827 
00828         // Builds the list of sides eligible for choice (nonrandom factions)
00829         std::vector<int> nonrandom_sides;
00830         for(config::child_iterator itor = parent_->era_sides_.begin();
00831                 itor != parent_->era_sides_.end(); ++itor) {
00832             if((**itor)["random_faction"] != "yes") {
00833                 const std::string& faction_id = (**itor)["id"];
00834                 if (
00835                     !faction_choices.empty() &&
00836                     std::find(faction_choices.begin(),faction_choices.end(),faction_id) == faction_choices.end()
00837                 )
00838                     continue;
00839                 if (
00840                     !faction_excepts.empty() &&
00841                     std::find(faction_excepts.begin(),faction_excepts.end(),faction_id) != faction_excepts.end()
00842                 )
00843                     continue;
00844                 nonrandom_sides.push_back(itor - parent_->era_sides_.begin());
00845             }
00846         }
00847 
00848         if (nonrandom_sides.size() == 0) {
00849             throw config::error(_("Only random sides in the current era."));
00850         }
00851 
00852         faction_ = nonrandom_sides[rand() % nonrandom_sides.size()];
00853     }
00854     bool solved_random_leader = false;
00855 
00856     if (llm_.get_leader() == "random") {
00857         // Choose a random leader type, and force gender to be random
00858         llm_.set_gender("random");
00859         const config& fact = *parent_->era_sides_[faction_];
00860         std::vector<std::string> types = utils::split(fact["random_leader"]);
00861         if (!types.empty()) {
00862             const int lchoice = rand() % types.size();
00863             leader_ = types[lchoice];
00864         } else {
00865             // If random_leader= doesn't exists, we use leader=
00866             types = utils::split(fact["leader"]);
00867             if (!types.empty()) {
00868                 const int lchoice = rand() % types.size();
00869                 leader_ = types[lchoice];
00870             } else {
00871                 utils::string_map i18n_symbols;
00872                 i18n_symbols["faction"] = fact["name"];
00873                 throw config::error(vgettext("Unable to find a leader type for faction $faction", i18n_symbols));
00874             }
00875         }
00876         solved_random_leader = true;
00877     }
00878     // Resolve random genders "very much" like standard unit code
00879     if (llm_.get_gender() == "random" || solved_random_leader) {
00880         unit_type_data::unit_type_map::const_iterator ut = unit_type_data::types().find(leader_.empty() ? llm_.get_leader() : leader_);
00881 
00882         if (ut != unit_type_data::types().end()) {
00883             const std::vector<unit_race::GENDER> glist = ut->second.genders();
00884             if (!glist.empty()) {
00885                 const int gchoice = rand() % glist.size();
00886                 // Pick up a gender, using the random 'gchoice' index
00887                 unit_race::GENDER sgender = glist[gchoice];
00888                 switch (sgender)
00889                 {
00890                     case unit_race::FEMALE:
00891                         gender_ = "female";
00892                         break;
00893                     case unit_race::MALE:
00894                         gender_ = "male";
00895                         break;
00896                     default:
00897                         gender_ = "null";
00898                 }
00899             } else gender_ = "null";
00900         } else {
00901             ERR_CF << "cannot obtain genders for invalid leader '" << (leader_.empty() ? llm_.get_leader() : leader_) << "'.\n";
00902             gender_ = "null";
00903         }
00904     }
00905 }
00906 
00907 connect::connect(game_display& disp, const config& game_config,
00908         chat& c, config& gamelist, const create::parameters& params,
00909         mp::controller default_controller) :
00910     mp::ui(disp, _("Game Lobby: ") + params.name, game_config, c, gamelist),
00911     level_(),
00912     state_(),
00913     params_(params),
00914     era_sides_(),
00915     player_types_(),
00916     player_factions_(),
00917     player_teams_(),
00918     player_colours_(),
00919     ai_algorithms_(),
00920     team_names_(),
00921     user_team_names_(),
00922     team_prefix_(std::string(_("Team")) + " "),
00923     sides_(),
00924     users_(),
00925     waiting_label_(video(), "", font::SIZE_SMALL, font::LOBBY_COLOUR),
00926     message_full_(false),
00927     default_controller_(default_controller),
00928     scroll_pane_(video()),
00929     type_title_label_(video(), _("Player/Type"), font::SIZE_SMALL, font::LOBBY_COLOUR),
00930     faction_title_label_(video(), _("Faction"), font::SIZE_SMALL, font::LOBBY_COLOUR),
00931     team_title_label_(video(), _("Team/Gender"), font::SIZE_SMALL, font::LOBBY_COLOUR),
00932     colour_title_label_(video(), _("Color"), font::SIZE_SMALL, font::LOBBY_COLOUR),
00933     gold_title_label_(video(), _("Gold"), font::SIZE_SMALL, font::LOBBY_COLOUR),
00934     income_title_label_(video(), _("Income"), font::SIZE_SMALL, font::LOBBY_COLOUR),
00935 
00936     launch_(video(), _("I'm Ready")),
00937     cancel_(video(), _("Cancel"))
00938 {
00939     load_game();
00940 
00941     if(get_result() == QUIT
00942         || get_result() == CREATE)
00943         return;
00944     lists_init();
00945     if(sides_.empty()) {
00946         throw config::error(_("The scenario is invalid because it has no sides."));
00947     }
00948     // Send Initial information
00949     config response;
00950     config& create_game = response.add_child("create_game");
00951     create_game["name"] = params.name;
00952     if(params.password.empty() == false) {
00953         create_game["password"] = params.password;
00954     }
00955 /*
00956     // The number of human-controlled sides is important to know
00957     // to let the server decide how many players can join this game
00958     int human_sides = 0;
00959     config::child_list cfg_sides = current_config()->get_children("side");
00960     for (config::child_list::const_iterator side = cfg_sides.begin(); side != cfg_sides.end(); side++){
00961         if ((**side)["controller"] == "human"){
00962             human_sides++;
00963         }
00964     }
00965     create_game["human_sides"] = lexical_cast<std::string>(human_sides);*/
00966     network::send_data(response, 0, true);
00967 
00968     // Adds the current user as default user.
00969     users_.push_back(connected_user(preferences::login(), CNTR_LOCAL, 0));
00970     update_user_combos();
00971     // Take the first available side or available side with id == login
00972     int side_choice = -1;
00973     for(side_list::const_iterator s = sides_.begin(); s != sides_.end(); ++s) {
00974         if (s->allow_player()) {
00975             if (side_choice == -1)
00976                 side_choice = s - sides_.begin();
00977             if(s->get_save_id() == preferences::login()) {
00978                 side_choice = s - sides_.begin();
00979                 break;
00980             }
00981         }
00982     }
00983 
00984     if (side_choice != -1)
00985     {
00986         sides_[side_choice].set_id(preferences::login());
00987     }
00988 
00989     // Updates the "level_" variable, now that sides are loaded
00990     update_level();
00991     update_playerlist_state(true);
00992 
00993     // If we are connected, send data to the connected host
00994     network::send_data(level_, 0, true);
00995 }
00996 
00997 void connect::process_event()
00998 {
00999     bool changed = false;
01000 
01001     for(size_t n = 0; n != sides_.size(); ++n) {
01002         sides_[n].process_event();
01003         if (sides_[n].changed())
01004             changed = true;
01005     }
01006 
01007     if (cancel_.pressed()) {
01008         if(network::nconnections() > 0) {
01009             config cfg;
01010             cfg.add_child("leave_game");
01011             network::send_data(cfg, 0, true);
01012         }
01013 
01014         set_result(QUIT);
01015         return;
01016     }
01017 
01018     if (launch_.pressed()) {
01019         if (!sides_available())
01020             set_result(mp::ui::PLAY);
01021     }
01022 
01023     // If something has changed in the level config,
01024     // send it to the network:
01025     if (changed) {
01026         update_playerlist_state();
01027         update_and_send_diff();
01028     }
01029 }
01030 
01031 const game_state& connect::get_state()
01032 {
01033     return state_;
01034 }
01035 
01036 void connect::start_game()
01037 {
01038     // Resolves the "random faction", "random gender" and "random message"
01039     for (side_list::iterator itor = sides_.begin(); itor != sides_.end();
01040             ++itor) {
01041 
01042         itor->resolve_random();
01043     }
01044     // Make other clients not show the results of resolve_random().
01045     config lock;
01046     lock.add_child("stop_updates");
01047     network::send_data(lock, 0, true);
01048     update_and_send_diff(true);
01049 
01050     // Build the gamestate object after updating the level
01051 
01052     level_to_gamestate(level_, state_, params_.saved_game);
01053 
01054     // add era events only if not save
01055     const config* const era_cfg = level_.child("era");
01056     if (era_cfg != NULL && level_["savegame"] != "yes") {
01057         game_events::add_events(era_cfg->get_children("event"),"era_events");
01058     }
01059 
01060     network::send_data(config("start_game"), 0, true);
01061 }
01062 
01063 void connect::hide_children(bool hide)
01064 {
01065     ui::hide_children(hide);
01066 
01067     waiting_label_.hide(hide);
01068     // Hiding the scrollpane automatically hides its contents
01069     scroll_pane_.hide(hide);
01070     for (side_list::iterator itor = sides_.begin(); itor != sides_.end(); ++itor) {
01071         itor->hide_ai_algorithm_combo(hide);
01072     }
01073     faction_title_label_.hide(hide);
01074     team_title_label_.hide(hide);
01075     colour_title_label_.hide(hide);
01076     if (!params_.saved_game) {
01077         gold_title_label_.hide(hide);
01078         income_title_label_.hide(hide);
01079     }
01080 
01081     launch_.hide(hide);
01082     cancel_.hide(hide);
01083 }
01084 
01085 void connect::process_network_data(const config& data, const network::connection sock)
01086 {
01087     ui::process_network_data(data, sock);
01088 
01089     if(data.child("leave_game")) {
01090         set_result(QUIT);
01091         return;
01092     }
01093 
01094     if (!data["side_drop"].empty()) {
01095         const int side_drop = lexical_cast_default<int>(data["side_drop"], 0) - 1;
01096         if(side_drop >= 0 && side_drop < int(sides_.size())) {
01097             connected_user_list::iterator player = find_player(sides_[side_drop].get_id());
01098             sides_[side_drop].reset(sides_[side_drop].get_controller());
01099             if (player != users_.end()) {
01100                 users_.erase(player);
01101                 update_user_combos();
01102             }
01103             update_and_send_diff();
01104             return;
01105         }
01106     }
01107 
01108     if (!data["side"].empty()) {
01109         int side_taken = lexical_cast_default<int>(data["side"], 0) - 1;
01110 
01111         // Checks if the connecting user has a valid and unique name.
01112         const std::string name = data["name"];
01113         if(name.empty()) {
01114             config response;
01115             response.values["failed"] = "yes";
01116             network::send_data(response, sock, true);
01117             ERR_CF << "ERROR: No username provided with the side.\n";
01118             return;
01119         }
01120 
01121         connected_user_list::iterator player = find_player(name);
01122         if(player != users_.end()) {
01123             //! @todo Seems like a needless limitation to only allow one side
01124             //! per player.
01125             if(find_player_side(name) != -1) {
01126                 config response;
01127                 response.values["failed"] = "yes";
01128                 response["message"] = "The nick '" + name + "' is already in use.";
01129                 network::send_data(response, sock, true);
01130                 return;
01131             } else {
01132                 users_.erase(player);
01133                 config observer_quit;
01134                 observer_quit.add_child("observer_quit").values["name"] = name;
01135                 network::send_data(observer_quit, 0, true);
01136                 update_user_combos();
01137             }
01138         }
01139 
01140         // Assigns this user to a side
01141         if(side_taken >= 0 && side_taken < int(sides_.size())) {
01142             if(!sides_[side_taken].available()) {
01143                 // This side is already taken.
01144                 // Try to reassing the player to a different position.
01145                 side_list::const_iterator itor;
01146                 side_taken = 0;
01147                 for (itor = sides_.begin(); itor != sides_.end();
01148                         ++itor, ++side_taken) {
01149                     if(itor->available())
01150                         break;
01151                 }
01152 
01153                 if(itor == sides_.end()) {
01154                     config response;
01155                     response.values["failed"] = "yes";
01156                     network::send_data(response, sock, true);
01157                     config kick;
01158                     kick["username"] = data["name"];
01159                     config res;
01160                     res.add_child("kick", kick);
01161                     network::send_data(res, 0, true);
01162                     update_user_combos();
01163                     update_and_send_diff();
01164                     ERR_CF << "ERROR: Couldn't assign a side to '" << name << "'\n";
01165                     return;
01166                 }
01167             }
01168 
01169             LOG_CF << "client has taken a valid position\n";
01170 
01171             // Adds the name to the list
01172             users_.push_back(connected_user(name, CNTR_NETWORK, sock));
01173             update_user_combos();
01174 
01175             // sides_[side_taken].set_connection(sock);
01176             sides_[side_taken].import_network_user(data);
01177             update_playerlist_state(false);
01178             update_and_send_diff();
01179 
01180             LOG_NW << "sent player data\n";
01181 
01182         } else {
01183             ERR_CF << "tried to take illegal side: " << side_taken << '\n';
01184             config response;
01185             response.values["failed"] = "yes";
01186             network::send_data(response, sock, true);
01187         }
01188     }
01189 
01190     const config* change_faction = data.child("change_faction");
01191     if(change_faction != NULL) {
01192         int side_taken = find_player_side(change_faction->get_attribute("name"));
01193         if(side_taken != -1) {
01194             sides_[side_taken].import_network_user(*change_faction);
01195             update_playerlist_state();
01196             update_and_send_diff();
01197         }
01198     }
01199 
01200     if(data.child("observer") != NULL) {
01201         const t_string& observer_name = data.child("observer")->get_attribute("name");
01202         if(!observer_name.empty()) {
01203             connected_user_list::iterator player = find_player(observer_name);
01204             if(player == users_.end()) {
01205                 users_.push_back(connected_user(observer_name, CNTR_NETWORK, sock));
01206                 update_user_combos();
01207                 update_playerlist_state();
01208                 update_and_send_diff();
01209             }
01210         }
01211     }
01212     if(data.child("observer_quit") != NULL) {
01213         const t_string& observer_name = data.child("observer_quit")->get_attribute("name");
01214         if(!observer_name.empty()) {
01215             connected_user_list::iterator player = find_player(observer_name);
01216             if(player != users_.end() && find_player_side(observer_name) == -1) {
01217                 users_.erase(player);
01218                 update_user_combos();
01219                 update_playerlist_state();
01220                 update_and_send_diff();
01221             }
01222         }
01223     }
01224 }
01225 
01226 void connect::process_network_error(network::error& error)
01227 {
01228     // If the problem isn't related to any specific connection,
01229     // it's a general error and we should just re-throw the error.
01230     // Likewise if we are not a server, we cannot afford any connection
01231     // to go down, so also re-throw the error.
01232     if(!error.socket || !network::is_server()) {
01233         error.disconnect();
01234         throw network::error(error.message);
01235     }
01236 
01237     bool changes = false;
01238 
01239     // A socket has disconnected. Remove it, and resets its side
01240     connected_user_list::iterator user;
01241     for(user = users_.begin(); user != users_.end(); ++user) {
01242         if(user->connection == error.socket) {
01243             changes = true;
01244 
01245             int i = find_player_side(user->name);
01246             if (i != -1)
01247                 sides_[i].reset(default_controller_);
01248 
01249             break;
01250         }
01251     }
01252     if(user != users_.end()) {
01253         users_.erase(user);
01254         update_user_combos();
01255     }
01256 
01257     // Now disconnect the socket
01258     error.disconnect();
01259 
01260     // If there have been changes to the positions taken,
01261     // then notify other players
01262     if(changes) {
01263         update_and_send_diff();
01264         update_playerlist_state();
01265     }
01266 }
01267 
01268 bool connect::accept_connections()
01269 {
01270     if (sides_available())
01271         return true;
01272     return false;
01273 }
01274 
01275 void connect::process_network_connection(const network::connection sock)
01276 {
01277     ui::process_network_connection(sock);
01278 
01279     network::send_data(config("join_game"), 0, true);
01280 
01281     network::send_data(level_, sock, true);
01282 }
01283 
01284 void connect::layout_children(const SDL_Rect& rect)
01285 {
01286     ui::layout_children(rect);
01287 
01288     SDL_Rect ca = client_area();
01289 
01290     gui::button* left_button = &launch_;
01291     gui::button* right_button = &cancel_;
01292 #ifdef OK_BUTTON_ON_RIGHT
01293     std::swap(left_button,right_button);
01294 #endif
01295     size_t left = ca.x;
01296     size_t right = ca.x + ca.w;
01297     size_t top = ca.y;
01298     size_t bottom = ca.y + ca.h;
01299 
01300     // Buttons
01301     right_button->set_location(right  - right_button->width(),
01302                                bottom - right_button->height());
01303     left_button->set_location(right  - right_button->width() - left_button->width() - gui::ButtonHPadding,
01304                               bottom - left_button->height());
01305 
01306     waiting_label_.set_location(left + 8, bottom-left_button->height() + 4);
01307     type_title_label_.set_location(left+30, top+35);
01308     faction_title_label_.set_location((left+145), top+35);
01309     team_title_label_.set_location((left+260), top+35);
01310     colour_title_label_.set_location((left+375), top+35);
01311     gold_title_label_.set_location((left+493), top+35);
01312     income_title_label_.set_location((left+560), top+35);
01313 
01314     SDL_Rect scroll_pane_rect;
01315     scroll_pane_rect.x = ca.x;
01316     scroll_pane_rect.y = ca.y + 50;
01317     scroll_pane_rect.w = ca.w;
01318     scroll_pane_rect.h = launch_.location().y - scroll_pane_rect.y - gui::ButtonVPadding;
01319 
01320     scroll_pane_.set_location(scroll_pane_rect);
01321 }
01322 
01323 void connect::lists_init()
01324 {
01325     // Options
01326     player_types_.push_back(_("Network Player"));
01327     player_types_.push_back(_("Local Player"));
01328     player_types_.push_back(_("Computer Player"));
01329     player_types_.push_back(_("Empty"));
01330 
01331     for(std::vector<config*>::const_iterator faction = era_sides_.begin();
01332                                                        faction != era_sides_.end();
01333                                                        ++faction) {
01334         player_factions_.push_back((**faction)["name"]);
01335     }
01336 
01337     // AI algorithms
01338     ai_algorithms_ = get_available_ais();
01339 
01340     // Factions
01341     const config::child_itors sides = current_config()->child_range("side");
01342 
01343     // Teams
01344     config::child_iterator sd;
01345     if(params_.use_map_settings) {
01346         for(sd = sides.first; sd != sides.second; ++sd) {
01347             const int side_num = sd - sides.first + 1;
01348             t_string& team_name = (**sd)["team_name"];
01349             t_string& user_team_name = (**sd)["user_team_name"];
01350 
01351             if(team_name.empty())
01352                 team_name = lexical_cast<std::string>(side_num);
01353 
01354             if(user_team_name.empty())
01355             {
01356                 user_team_name = team_name;
01357             }
01358 
01359             std::vector<std::string>::const_iterator itor = std::find(team_names_.begin(),
01360                                                             team_names_.end(), team_name);
01361             if(itor == team_names_.end()) {
01362                 team_names_.push_back(team_name);
01363                 user_team_names_.push_back(user_team_name);
01364                 player_teams_.push_back(user_team_name.str());
01365             }
01366         }
01367     } else {
01368         std::vector<std::string> map_team_names;
01369         for(sd = sides.first; sd != sides.second; ++sd) {
01370             const std::string side_num = lexical_cast<std::string>(sd - sides.first + 1);
01371             t_string& team_name = (**sd)["team_name"];
01372 
01373             if(team_name.empty())
01374                 team_name = side_num;
01375 
01376             std::vector<std::string>::const_iterator itor = std::find(map_team_names.begin(),
01377                                                             map_team_names.end(), team_name);
01378             if(itor == map_team_names.end()) {
01379                 map_team_names.push_back(team_name);
01380                 team_name = lexical_cast<std::string>(map_team_names.size());
01381             } else {
01382                 team_name = lexical_cast<std::string>(itor - map_team_names.begin() + 1);
01383             }
01384 
01385             team_names_.push_back(side_num);
01386             user_team_names_.push_back(team_prefix_ + side_num);
01387             player_teams_.push_back(team_prefix_ + side_num);
01388         }
01389     }
01390 
01391     // Colours
01392     for(int i = 0; i < gamemap::MAX_PLAYERS; ++i) {
01393         player_colours_.push_back(get_colour_string(i));
01394     }
01395 
01396     // Populates "sides_" from the level configuration
01397     sides_.reserve(sides.second - sides.first);
01398     int index = 0;
01399     for(sd = sides.first; sd != sides.second; ++sd, ++index) {
01400         sides_.push_back(side(*this, **sd, index));
01401     }
01402     int offset=0;
01403     // This function must be called after the sides_ vector is fully populated.
01404     for(side_list::iterator s = sides_.begin(); s != sides_.end(); ++s) {
01405         const int side_num = s - sides_.begin();
01406         const int spos = 60 * (side_num-offset);
01407         if(!s->allow_player()) {
01408             offset++;
01409             continue;
01410         }
01411 
01412         s->add_widgets_to_scrollpane(scroll_pane_, spos);
01413     }
01414 }
01415 
01416 //! Called by the constructor to initialize the game from a create::parameters structure.
01417 void connect::load_game()
01418 {
01419     if(params_.saved_game) {
01420         bool show_replay = false;
01421         //bool cancel_orders = false;
01422         const std::string game = dialogs::load_game_dialog(disp(),
01423                                  game_config(), &show_replay,
01424                                  NULL);
01425 
01426         if(game.empty()) {
01427             set_result(CREATE);
01428             return;
01429         }
01430 
01431         std::string error_log;
01432         {
01433             cursor::setter cur(cursor::WAIT);
01434 			::load_game(game, state_, &error_log);
01435         }
01436         if(!error_log.empty()) {
01437             gui::show_error_message(disp(),
01438                     _("The file you have tried to load is corrupt: '") +
01439                     error_log);
01440             set_result(CREATE);
01441             return;
01442         }
01443 
01444         if(state_.campaign_type != "multiplayer") {
01445             /* GCC-3.3 needs a temp var otherwise compilation fails */
01446             gui::message_dialog dlg(disp(), "", _("This is not a multiplayer save"));
01447             dlg.show();
01448             set_result(CREATE);
01449             return;
01450         }
01451 
01452         if(state_.version != game_config::version) {
01453             // Do not load if too old, but if either the savegame or
01454             // the current game has the version 'test' allow loading.
01455             if(state_.version < game_config::min_savegame_version &&
01456                     game_config::test_version.full != state_.version &&
01457                     game_config::test_version.full != game_config::version) {
01458 
01459                 /* GCC-3.3 needs a temp var otherwise compilation fails */
01460                 gui::message_dialog dlg2(disp(), "", _("This save is from a version too old to be loaded."));
01461                 dlg2.show();
01462                 set_result(CREATE);
01463                 return;
01464             }
01465 
01466             const int res = gui::dialog(disp(), "",
01467                     _("This save is from a different version of the game. Do you want to try to load it?"),
01468                     gui::YES_NO).show();
01469             if(res == 1) {
01470                 set_result(CREATE);
01471                 return;
01472             }
01473         }
01474 
01475         level_["savegame"] = "yes";
01476         // If we have a start of scenario MP campaign scenario the snapshot
01477         // is empty the starting position contains the wanted info.
01478         const config& start_data = !state_.snapshot.empty() ? state_.snapshot : state_.starting_pos;
01479         level_["map_data"] = start_data["map_data"];
01480         level_["id"] = start_data["id"];
01481         level_["name"] = start_data["name"];
01482         // Probably not needed.
01483         level_["turn"] = start_data["turn_at"];
01484         level_["turn_at"] = start_data["turn_at"];
01485         level_["turns"] = start_data["turns"];
01486         level_["mp_use_map_settings"] = start_data["mp_use_map_settings"];
01487         level_["mp_village_gold"] = start_data["mp_village_gold"];
01488         level_["mp_fog"] = start_data["mp_fog"];
01489         level_["mp_shroud"] = start_data["mp_shroud"];
01490         level_["mp_countdown"] = start_data["mp_countdown"];
01491         level_["mp_countdown_init_time"] = start_data["mp_countdown_init_time"];
01492         level_["mp_countdown_turn_bonus"] = start_data["mp_countdown_turn_bonus"];
01493         level_["mp_countdown_action_bonus"] = start_data["mp_countdown_action_bonus"];
01494         level_["experience_modifier"] = start_data["experience_modifier"];
01495         level_["observer"] = start_data["observer"];
01496         level_.add_child("snapshot") = start_data;
01497         level_["random_seed"] = start_data["random_seed"];
01498         level_["random_calls"] = start_data["random_calls"];
01499 
01500         // Adds the replay data, and the replay start, to the level,
01501         // so clients can receive it.
01502         level_.add_child("replay") = state_.replay_data;
01503         if(!state_.starting_pos.empty())
01504             level_.add_child("replay_start") = state_.starting_pos;
01505         level_.add_child("statistics") = statistics::write_stats();
01506 
01507     } else {
01508         level_["savegame"] = "no";
01509         level_ = params_.scenario_data;
01510 
01511         level_["hash"] = level_.hash();
01512         level_["turns"] = lexical_cast_default<std::string>(params_.num_turns, "20");
01513         level_["mp_countdown"] = params_.mp_countdown ? "yes" : "no";
01514         level_["mp_countdown_init_time"] = lexical_cast_default<std::string>(params_.mp_countdown_init_time, "270");
01515         level_["mp_countdown_turn_bonus"] = lexical_cast_default<std::string>(params_.mp_countdown_turn_bonus, "35");
01516         level_["mp_countdown_reservoir_time"] = lexical_cast_default<std::string>(params_.mp_countdown_reservoir_time, "330");
01517         level_["mp_countdown_action_bonus"] = lexical_cast_default<std::string>(params_.mp_countdown_action_bonus, "13");
01518 
01519         if (params_.random_start_time)
01520         {
01521             if (!gamestatus::is_start_ToD(level_["random_start_time"]))
01522             {
01523                 level_["random_start_time"] = "yes";
01524             }
01525         }
01526         else
01527         {
01528             level_["random_start_time"] = "no";
01529         }
01530 
01531         level_["scenario"] = params_.name;
01532         level_["experience_modifier"] = lexical_cast<std::string>(params_.xp_modifier);
01533         level_["random_seed"] = lexical_cast<std::string>(state_.rng().get_random_seed());
01534         level_["mp_village_gold"] = lexical_cast<std::string>(params_.village_gold);
01535         level_["mp_fog"] = params_.fog_game ? "yes" : "no";
01536         level_["mp_shroud"] = params_.shroud_game ? "yes" : "no";
01537         level_["mp_use_map_settings"] = params_.use_map_settings ? "yes" : "no";
01538     }
01539 
01540     // Add the map name to the title.
01541     append_to_title(" - " + level_["name"]);
01542 
01543 
01544     std::string era;
01545     if (params_.saved_game
01546         && level_.child("snapshot")->child("era"))
01547     {
01548         era = level_.child("snapshot")->child("era")->get_attribute("id");
01549     }
01550     else
01551     {
01552         era = params_.era;
01553     }
01554 
01555     // Initialize the list of sides available for the current era.
01556     const config* const era_cfg = game_config().find_child("era", "id", era);
01557     if(!era_cfg) {
01558         if (!params_.saved_game)
01559         {
01560             utils::string_map i18n_symbols;
01561             i18n_symbols["era"] = era;
01562             throw config::error(vgettext("Cannot find era $era", i18n_symbols));
01563         }
01564         // FIXME: @todo We should tell user about missing era but still load game
01565         WRN_CF << "Missing era in MP load game " << era << "\n";
01566 
01567     }
01568     if (era_cfg)
01569     {
01570         era_sides_ = era_cfg->get_children("multiplayer_side");
01571         level_.add_child("era", *era_cfg);
01572     }
01573 
01574     gold_title_label_.hide(params_.saved_game);
01575     income_title_label_.hide(params_.saved_game);
01576 
01577     // This will force connecting clients to be using the same version number as us.
01578     level_["version"] = game_config::version;
01579 
01580     level_["observer"] = params_.allow_observers ? "yes" : "no";
01581 
01582     if(level_["objectives"].empty()) {
01583         level_["objectives"] = t_string(N_("Victory:\n\
01584 @Defeat enemy leader(s)"), "wesnoth");
01585     }
01586 }
01587 
01588 config* connect::current_config(){
01589     config* cfg_level = NULL;
01590 
01591     if (level_.child("snapshot") != NULL){
01592         // Savegame
01593         cfg_level = level_.child("snapshot");
01594     }
01595     else{
01596         // Fresh game, no snapshot available
01597         cfg_level = &level_;
01598     }
01599 
01600     return cfg_level;
01601 }
01602 
01603 void connect::update_level()
01604 {
01605     // Import all sides into the level
01606     level_.clear_children("side");
01607     for(side_list::const_iterator itor = sides_.begin(); itor != sides_.end();
01608             ++itor) {
01609 
01610         level_.add_child("side", itor->get_config());
01611     }
01612 }
01613 
01614 void connect::update_and_send_diff(bool update_time_of_day)
01615 {
01616     config old_level = level_;
01617     update_level();
01618     if (update_time_of_day)
01619     {
01620         // Set random start ToD
01621         gamestatus game_status(level_,atoi(level_["turns"].c_str()),&state_);
01622     }
01623 
01624     config diff = level_.get_diff(old_level);
01625     if (!diff.empty()) {
01626         config scenario_diff;
01627         scenario_diff.add_child("scenario_diff", diff);
01628         network::send_data(scenario_diff, 0, true);
01629     }
01630 }
01631 
01632 bool connect::sides_available()
01633 {
01634     for(side_list::const_iterator itor = sides_.begin(); itor != sides_.end(); ++itor) {
01635         if (itor->available())
01636             return true;
01637     }
01638     return false;
01639 }
01640 
01641 void connect::update_playerlist_state(bool silent)
01642 {
01643     waiting_label_.set_text(sides_available() ? _("Waiting for players to join...") : "");
01644     launch_.enable(!sides_available());
01645 
01646     // If the "gamelist_" variable has users, use it.
01647     // Else, extracts the user list from the actual player list.
01648     if (gamelist().child("user") != NULL) {
01649         ui::gamelist_updated(silent);
01650     } else {
01651         // Updates the player list
01652         std::vector<std::string> playerlist;
01653         for(connected_user_list::const_iterator itor = users_.begin();
01654                                                        itor != users_.end();
01655                                                        ++itor) {
01656             playerlist.push_back(itor->name);
01657         }
01658         set_user_list(playerlist, silent);
01659         set_user_menu_items(playerlist);
01660     }
01661 }
01662 
01663 connect::connected_user_list::iterator connect::find_player(const std::string& id)
01664 {
01665     connected_user_list::iterator itor;
01666     for (itor = users_.begin(); itor != users_.end(); ++itor) {
01667         if (itor->name == id)
01668             break;
01669     }
01670     return itor;
01671 }
01672 
01673 int connect::find_player_side(const std::string& id) const
01674 {
01675     side_list::const_iterator itor;
01676     for (itor = sides_.begin(); itor != sides_.end(); ++itor) {
01677         if (itor->get_id() == id)
01678             break;
01679     }
01680 
01681     if (itor == sides_.end())
01682         return -1;
01683 
01684     return itor - sides_.begin();
01685 }
01686 
01687 void connect::update_user_combos()
01688 {
01689     for (side_list::iterator itor = sides_.begin();
01690                                     itor != sides_.end(); ++itor) {
01691         itor->update_user_list();
01692     }
01693 }
01694 
01695 void connect::kick_player(const std::string& name)
01696 {
01697     connected_user_list::iterator player = find_player(name);
01698     if(player == users_.end())
01699         return;
01700 
01701     if(player->controller != CNTR_NETWORK)
01702         return;
01703 
01704     // If we are the server, kick the user ourselves;
01705     // else, ask the server to do so.
01706     if(network::is_server()) {
01707         network::disconnect(player->connection);
01708     } else {
01709         config kick;
01710         kick["username"] = name;
01711         config res;
01712         res.add_child("kick", kick);
01713         network::send_data(res, 0, true);
01714     }
01715 
01716     int side = find_player_side(name);
01717     if (side != -1) {
01718         assert(size_t(side) < sides_.size());
01719         sides_[side].reset(sides_[side].get_controller());
01720     }
01721     users_.erase(player);
01722     update_user_combos();
01723 }
01724 
01725 } // end namespace mp
01726 

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