multiplayer_wait.cpp

Go to the documentation of this file.
00001 /* $Id: multiplayer_wait.cpp 52934 2012-02-06 20:27:13Z mordante $ */
00002 /*
00003    Copyright (C) 2007 - 2012
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 as published by
00008    the Free Software Foundation; either version 2 of the License, or
00009    (at your option) any later version.
00010    This program is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY.
00012 
00013    See the COPYING file for more details.
00014 */
00015 
00016 #include "global.hpp"
00017 
00018 #include "dialogs.hpp"
00019 #include "foreach.hpp"
00020 #include "gettext.hpp"
00021 #include "game_preferences.hpp"
00022 #include "gui/dialogs/transient_message.hpp"
00023 #include "game_display.hpp"
00024 #include "leader_list.hpp"
00025 #include "log.hpp"
00026 #include "marked-up_text.hpp"
00027 #include "multiplayer_wait.hpp"
00028 #include "statistics.hpp"
00029 #include "wml_exception.hpp"
00030 #include "wml_separators.hpp"
00031 #include "formula_string_utils.hpp"
00032 
00033 static lg::log_domain log_network("network");
00034 #define DBG_NW LOG_STREAM(debug, log_network)
00035 #define LOG_NW LOG_STREAM(info, log_network)
00036 
00037 namespace {
00038 const SDL_Rect leader_pane_position = {-260,-370,260,370};
00039 const int leader_pane_border = 10;
00040 }
00041 
00042 namespace mp {
00043 
00044 wait::leader_preview_pane::leader_preview_pane(game_display& disp,
00045         const std::vector<const config *> &side_list, int color) :
00046     gui::preview_pane(disp.video()),
00047     side_list_(side_list),
00048     color_(color),
00049     leader_combo_(disp, std::vector<std::string>()),
00050     gender_combo_(disp, std::vector<std::string>()),
00051     leaders_(side_list, &leader_combo_, &gender_combo_),
00052     selection_(0)
00053 {
00054     leaders_.set_color(color_);
00055     set_location(leader_pane_position);
00056 }
00057 
00058 void wait::leader_preview_pane::process_event()
00059 {
00060 
00061     if (leader_combo_.changed() || gender_combo_.changed()) {
00062         leaders_.set_leader_combo(&leader_combo_);
00063         leaders_.update_gender_list(leaders_.get_leader());
00064         set_dirty();
00065     }
00066 }
00067 
00068 void wait::leader_preview_pane::draw_contents()
00069 {
00070     bg_restore();
00071 
00072     surface screen = video().getSurface();
00073 
00074     SDL_Rect const &loc = location();
00075     const SDL_Rect area = create_rect(loc.x + leader_pane_border
00076             , loc.y + leader_pane_border
00077             , loc.w - leader_pane_border * 2
00078             , loc.h - leader_pane_border * 2);
00079     const clip_rect_setter clipper(screen, &area);
00080 
00081     if(selection_ < side_list_.size()) {
00082         const config& side = *side_list_[selection_];
00083         std::string faction = side["faction"];
00084 
00085         const std::string recruits = side["recruit"];
00086         const std::vector<std::string> recruit_list = utils::split(recruits);
00087         std::ostringstream recruit_string;
00088 
00089         if(!faction.empty() && faction[0] == font::IMAGE) {
00090             std::string::size_type p = faction.find_first_of(COLUMN_SEPARATOR);
00091             if(p != std::string::npos && p < faction.size())
00092                 faction = faction.substr(p+1);
00093         }
00094         std::string leader = leaders_.get_leader();
00095         std::string gender = leaders_.get_gender();
00096 
00097         std::string image;
00098 
00099         const unit_type *ut = unit_types.find(leader);
00100 
00101         if (ut) {
00102             const unit_type &utg = ut->get_gender_unit_type(gender);
00103 
00104             image = utg.image() + leaders_.get_RC_suffix(utg.flag_rgb());
00105         }
00106 
00107         for(std::vector<std::string>::const_iterator itor = recruit_list.begin();
00108                 itor != recruit_list.end(); ++itor) {
00109             const unit_type *rt = unit_types.find(*itor);
00110             if (!rt) continue;
00111 
00112             if(itor != recruit_list.begin())
00113                 recruit_string << ", ";
00114             recruit_string << rt->type_name();
00115         }
00116 
00117         SDL_Rect image_rect = {area.x,area.y,0,0};
00118 
00119         surface unit_image(image::get_image(image));
00120 
00121         if(!unit_image.null()) {
00122             image_rect.w = unit_image->w;
00123             image_rect.h = unit_image->h;
00124             sdl_blit(unit_image,NULL,screen,&image_rect);
00125         }
00126 
00127         font::draw_text(&video(),area,font::SIZE_PLUS,font::NORMAL_COLOR,faction,area.x + 110, area.y + 60);
00128         const SDL_Rect leader_rect = font::draw_text(&video(),area,font::SIZE_SMALL,font::NORMAL_COLOR,
00129                 _("Leader: "),area.x, area.y + 110);
00130         const SDL_Rect gender_rect = font::draw_text(&video(),area,font::SIZE_SMALL,font::NORMAL_COLOR,
00131                 _("Gender: "),area.x, leader_rect.y + 30 + (leader_rect.h - leader_combo_.height()) / 2);
00132         font::draw_wrapped_text(&video(),area,font::SIZE_SMALL,font::NORMAL_COLOR,
00133                 _("Recruits: ") + recruit_string.str(),area.x, area.y + 132 + 30 + (leader_rect.h - leader_combo_.height()) / 2,
00134                 area.w);
00135         leader_combo_.set_location(leader_rect.x + leader_rect.w + 16, leader_rect.y + (leader_rect.h - leader_combo_.height()) / 2);
00136         gender_combo_.set_location(leader_rect.x + leader_rect.w + 16, gender_rect.y + (gender_rect.h - gender_combo_.height()) / 2);
00137     }
00138 }
00139 
00140 bool wait::leader_preview_pane::show_above() const
00141 {
00142     return false;
00143 }
00144 
00145 bool wait::leader_preview_pane::left_side() const
00146 {
00147     return false;
00148 }
00149 
00150 void wait::leader_preview_pane::set_selection(int selection)
00151 {
00152     selection_ = selection;
00153     leaders_.update_leader_list(selection_);
00154     leaders_.update_gender_list(leaders_.get_leader());
00155     set_dirty();
00156 }
00157 
00158 std::string wait::leader_preview_pane::get_selected_leader()
00159 {
00160     return leaders_.get_leader();
00161 }
00162 
00163 std::string wait::leader_preview_pane::get_selected_gender()
00164 {
00165     return leaders_.get_gender();
00166 }
00167 
00168 handler_vector wait::leader_preview_pane::handler_members() {
00169     handler_vector h;
00170     h.push_back(&leader_combo_);
00171     h.push_back(&gender_combo_);
00172     return h;
00173 }
00174 
00175 
00176 wait::wait(game_display& disp, const config& cfg,
00177         mp::chat& c, config& gamelist) :
00178     ui(disp, _("Game Lobby"), cfg, c, gamelist),
00179     cancel_button_(disp.video(), _("Cancel")),
00180     start_label_(disp.video(), _("Waiting for game to start..."), font::SIZE_SMALL, font::LOBBY_COLOR),
00181     game_menu_(disp.video(), std::vector<std::string>(), false, -1, -1, NULL, &gui::menu::bluebg_style),
00182     level_(),
00183     state_(),
00184     stop_updates_(false)
00185 {
00186     game_menu_.set_numeric_keypress_selection(false);
00187     gamelist_updated();
00188 }
00189 
00190 void wait::process_event()
00191 {
00192     if (cancel_button_.pressed())
00193         set_result(QUIT);
00194 }
00195 
00196 void wait::join_game(bool observe)
00197 {
00198     //if we have got valid side data
00199     //the first condition is to make sure that we don't have another
00200     //WML message with a side-tag in it
00201     while (!level_.has_attribute("version") || !level_.child("side")) {
00202         network::connection data_res = dialogs::network_receive_dialog(disp(),
00203                 _("Getting game data..."), level_);
00204         if (!data_res) {
00205             set_result(QUIT);
00206             return;
00207         }
00208         check_response(data_res, level_);
00209         if(level_.child("leave_game")) {
00210             set_result(QUIT);
00211             return;
00212         }
00213     }
00214 
00215     // Add the map name to the title.
00216     append_to_title(": " + level_["name"].t_str());
00217 
00218     if (!observe) {
00219         //search for an appropriate vacant slot. If a description is set
00220         //(i.e. we're loading from a saved game), then prefer to get the side
00221         //with the same description as our login. Otherwise just choose the first
00222         //available side.
00223         const config *side_choice = NULL;
00224         int side_num = -1, nb_sides = 0;
00225         foreach (const config &sd, level_.child_range("side"))
00226         {
00227             if (sd["controller"] == "reserved" && sd["current_player"] == preferences::login())
00228             {
00229                 side_choice = &sd;
00230                 side_num = nb_sides;
00231                 break;
00232             }
00233             if (sd["controller"] == "network" && sd["player_id"].empty())
00234             {
00235                 if (!side_choice) { // found the first empty side
00236                     side_choice = &sd;
00237                     side_num = nb_sides;
00238                 }
00239                 if (sd["current_player"] == preferences::login()) {
00240                     side_choice = &sd;
00241                     side_num = nb_sides;
00242                     break;  // found the preferred one
00243                 }
00244             }
00245             ++nb_sides;
00246         }
00247         if (!side_choice) {
00248             set_result(QUIT);
00249             return;
00250         }
00251 
00252         bool allow_changes = (*side_choice)["allow_changes"].to_bool(true);
00253 
00254         //if the client is allowed to choose their team, instead of having
00255         //it set by the server, do that here.
00256         std::string leader_choice, gender_choice;
00257 
00258         if(allow_changes) {
00259             events::event_context context;
00260 
00261             const config &era = level_.child("era");
00262             /** @todo Check whether we have the era. If we don't inform the user. */
00263             if (!era)
00264                 throw config::error(_("No era information found."));
00265             config::const_child_itors possible_sides = era.child_range("multiplayer_side");
00266             if (possible_sides.first == possible_sides.second) {
00267                 set_result(QUIT);
00268                 throw config::error(_("No multiplayer sides found"));
00269             }
00270 
00271             int color = side_num;
00272             const std::string color_str = (*side_choice)["color"];
00273             if (!color_str.empty())
00274                 color = game_config::color_info(color_str).index() - 1;
00275 
00276             std::vector<const config *> leader_sides;
00277             foreach (const config &side, possible_sides) {
00278                 leader_sides.push_back(&side);
00279             }
00280 
00281             int forced_faction = find_suitable_faction(leader_sides, *side_choice);
00282             if (forced_faction >= 0) {
00283                 const config *f = leader_sides[forced_faction];
00284                 leader_sides.clear();
00285                 leader_sides.push_back(f);
00286             }
00287 
00288             std::vector<std::string> choices;
00289             foreach (const config *s, leader_sides)
00290             {
00291                 const config &side = *s;
00292                 const std::string &name = side["name"];
00293                 const std::string &icon = side["image"];
00294 
00295                 if (!icon.empty()) {
00296                     std::string rgb = side["flag_rgb"];
00297                     if (rgb.empty())
00298                         rgb = "magenta";
00299 
00300                     choices.push_back(IMAGE_PREFIX + icon + "~RC(" + rgb + ">" +
00301                         lexical_cast<std::string>(color+1) + ")" + COLUMN_SEPARATOR + name);
00302                 } else {
00303                     choices.push_back(name);
00304                 }
00305             }
00306 
00307             std::vector<gui::preview_pane* > preview_panes;
00308             leader_preview_pane leader_selector(disp(), leader_sides, color);
00309             preview_panes.push_back(&leader_selector);
00310 
00311             const int res = gui::show_dialog(disp(), NULL, _("Choose your faction:"), _("Starting position: ") + lexical_cast<std::string>(side_num + 1),
00312                         gui::OK_CANCEL, &choices, &preview_panes);
00313             if(res < 0) {
00314                 set_result(QUIT);
00315                 return;
00316             }
00317             const int faction_choice = res;
00318             leader_choice = leader_selector.get_selected_leader();
00319             gender_choice = leader_selector.get_selected_gender();
00320 
00321             assert(static_cast<unsigned>(faction_choice) < leader_sides.size());
00322 
00323             config faction;
00324             config& change = faction.add_child("change_faction");
00325             change["name"] = preferences::login();
00326             change["faction"] = forced_faction >= 0 ? forced_faction : faction_choice;
00327             change["leader"] = leader_choice;
00328             change["gender"] = gender_choice;
00329             network::send_data(faction, 0);
00330         }
00331 
00332     }
00333 
00334     generate_menu();
00335 }
00336 
00337 const game_state& wait::get_state()
00338 {
00339     return state_;
00340 }
00341 
00342 void wait::start_game()
00343 {
00344     if (const config &stats = level_.child("statistics")) {
00345         statistics::fresh_stats();
00346         statistics::read_stats(stats);
00347     }
00348 
00349     /**
00350      * @todo Instead of using level_to_gamestate reinit the state_,
00351      * this needs more testing -- Mordante
00352      * It seems level_to_gamestate is needed for the start of game
00353      * download, but downloads of later scenarios miss certain info
00354      * and add a players section. Use players to decide between old
00355      * and new way. (Of course it would be nice to unify the data
00356      * stored.)
00357      */
00358     if (!level_.child("player")) {
00359         level_to_gamestate(level_, state_);
00360     } else {
00361 
00362         state_ = game_state(level_);
00363 
00364         // When we observe and don't have the addon installed we still need
00365         // the old way, no clue why however. Code is a copy paste of
00366         // playcampaign.cpp:576 which shows an 'Unknown scenario: '$scenario|'
00367         // error. This seems to work and have no side effects....
00368         if(!state_.classification().scenario.empty() && state_.classification().scenario != "null") {
00369             DBG_NW << "Falling back to loading the old way.\n";
00370             level_to_gamestate(level_, state_);
00371         }
00372     }
00373 
00374     LOG_NW << "starting game\n";
00375 }
00376 
00377 void wait::layout_children(const SDL_Rect& rect)
00378 {
00379     ui::layout_children(rect);
00380 
00381     const SDL_Rect ca = client_area();
00382     int y = ca.y + ca.h - cancel_button_.height();
00383 
00384     game_menu_.set_location(ca.x, ca.y + title().height());
00385     game_menu_.set_measurements(ca.w, y - ca.y - title().height()
00386             - gui::ButtonVPadding);
00387     game_menu_.set_max_width(ca.w);
00388     game_menu_.set_max_height(y - ca.y - title().height() - gui::ButtonVPadding);
00389     cancel_button_.set_location(ca.x + ca.w - cancel_button_.width(), y);
00390     start_label_.set_location(ca.x, y + 4);
00391 }
00392 
00393 void wait::hide_children(bool hide)
00394 {
00395     ui::hide_children(hide);
00396 
00397     cancel_button_.hide(hide);
00398     game_menu_.hide(hide);
00399 }
00400 
00401 void wait::process_network_data(const config& data, const network::connection sock)
00402 {
00403     ui::process_network_data(data, sock);
00404 
00405     if(data["message"] != "") {
00406         gui2::show_transient_message(disp().video()
00407                 , _("Response")
00408                 , data["message"]);
00409     }
00410     if (data["failed"].to_bool()) {
00411         set_result(QUIT);
00412         return;
00413     } else if(data.child("stop_updates")) {
00414         stop_updates_ = true;
00415     } else if(data.child("start_game")) {
00416         LOG_NW << "received start_game message\n";
00417         set_result(PLAY);
00418         return;
00419     } else if(data.child("leave_game")) {
00420         set_result(QUIT);
00421         return;
00422     } else if (const config &c = data.child("scenario_diff")) {
00423         LOG_NW << "received diff for scenario... applying...\n";
00424         /** @todo We should catch config::error and then leave the game. */
00425         level_.apply_diff(c);
00426         generate_menu();
00427     } else if(data.child("side")) {
00428         level_ = data;
00429         LOG_NW << "got some sides. Current number of sides = "
00430             << level_.child_count("side") << ','
00431             << data.child_count("side") << '\n';
00432         generate_menu();
00433     }
00434 }
00435 
00436 void wait::generate_menu()
00437 {
00438     if (stop_updates_)
00439         return;
00440 
00441     std::vector<std::string> details;
00442     std::vector<std::string> playerlist;
00443 
00444     foreach (const config &sd, level_.child_range("side"))
00445     {
00446         if (!sd["allow_player"].to_bool(true)) {
00447             continue;
00448         }
00449 
00450         std::string description = sd["user_description"];
00451 
00452         t_string side_name = sd["faction_name"];
00453         std::string leader_type = sd["type"];
00454         std::string gender_id = sd["gender"];
00455 
00456         // Hack: if there is a unit which can recruit, use it as a
00457         // leader. Necessary to display leader information when loading
00458         // saves.
00459         foreach (const config &side_unit, sd.child_range("unit"))
00460         {
00461             if (side_unit["canrecruit"].to_bool()) {
00462                 leader_type = side_unit["type"].str();
00463                 break;
00464             }
00465         }
00466 
00467         if(!sd["player_id"].empty())
00468             playerlist.push_back(sd["player_id"]);
00469 
00470         std::string leader_name;
00471         std::string leader_image;
00472 
00473         const unit_type *ut = unit_types.find(leader_type);
00474 
00475         if (ut) {
00476             const unit_type &utg = ut->get_gender_unit_type(gender_id);
00477 
00478             leader_name = utg.type_name();
00479 #ifdef LOW_MEM
00480             leader_image = utg.image();
00481 #else
00482             std::string RCcolor = sd["color"];
00483 
00484             if (RCcolor.empty())
00485                 RCcolor = sd["side"].str();
00486             leader_image = utg.image() + std::string("~RC(") + std::string(utg.flag_rgb() + ">" + RCcolor + ")");
00487 #endif
00488         } else {
00489             leader_image = leader_list_manager::random_enemy_picture;
00490         }
00491         if (!leader_image.empty()) {
00492             // Dumps the "image" part of the faction name, if any,
00493             // to replace it by a picture of the actual leader
00494             if(side_name.str()[0] == font::IMAGE) {
00495                 std::string::size_type p =
00496                     side_name.str().find_first_of(COLUMN_SEPARATOR);
00497                 if(p != std::string::npos && p < side_name.size()) {
00498                     side_name = IMAGE_PREFIX + leader_image + COLUMN_SEPARATOR + side_name.str().substr(p+1);
00499                 }
00500             } else {
00501                 // no image prefix, just add the leader image
00502                 // (assuming that there is also no COLUMN_SEPARATOR)
00503                 side_name = IMAGE_PREFIX + leader_image + COLUMN_SEPARATOR + side_name.str();
00504             }
00505         }
00506 
00507         std::stringstream str;
00508         str << sd["side"] << ". " << COLUMN_SEPARATOR;
00509         str << description << COLUMN_SEPARATOR << side_name << COLUMN_SEPARATOR;
00510         // Mark parentheses translatable for languages like Japanese
00511         if(!leader_name.empty())
00512             str << _("(") << leader_name << _(")");
00513         str << COLUMN_SEPARATOR;
00514         // Don't show gold for saved games
00515         if (sd["allow_changes"].to_bool())
00516             str << sd["gold"] << ' ' << _n("multiplayer_starting_gold^Gold", "multiplayer_starting_gold^Gold", sd["gold"].to_int()) << COLUMN_SEPARATOR;
00517 
00518         int income_amt = sd["income"];
00519         if(income_amt != 0){
00520             str << _("(") << _("Income") << ' ';
00521             if(income_amt > 0)
00522                 str << _("+");
00523             str << sd["income"] << _(")");
00524         }
00525 
00526         str << COLUMN_SEPARATOR << t_string::from_serialized(sd["user_team_name"].str());
00527 
00528         int disp_color = sd["color"];
00529         if(!sd["color"].empty()) {
00530             try {
00531                 disp_color = game_config::color_info(sd["color"]).index();
00532             } catch(config::error&) {
00533                 //ignore
00534             }
00535         } else {
00536             /**
00537              * @todo we fall back to the side color, but that's ugly rather
00538              * make the color mandatory in 1.5.
00539              */
00540             disp_color = sd["side"];
00541         }
00542         str << COLUMN_SEPARATOR << get_color_string(disp_color - 1);
00543         details.push_back(str.str());
00544     }
00545 
00546     game_menu_.set_items(details);
00547 
00548     // Uses the actual connected player list if we do not have any
00549     // "gamelist" user data
00550     if (!gamelist().child("user")) {
00551         set_user_list(playerlist, true);
00552     }
00553 }
00554 
00555 } // namespace mp
00556 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines

Generated by doxygen 1.7.1 on Fri May 25 2012 01:03:06 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs