multiplayer_ui.cpp

Go to the documentation of this file.
00001 /* $Id: multiplayer_ui.cpp 52533 2012-01-07 02:35:17Z shadowmaster $ */
00002 /*
00003    Copyright (C) 2005 - 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 "construct_dialog.hpp"
00019 #include "foreach.hpp"
00020 #include "gamestatus.hpp"
00021 #include "game_display.hpp"
00022 #include "game_preferences.hpp"
00023 #include "gettext.hpp"
00024 #include "gui/dialogs/mp_cmd_wrapper.hpp"
00025 #include "lobby_preferences.hpp"
00026 #include "log.hpp"
00027 #include "marked-up_text.hpp"
00028 #include "menu_events.hpp"
00029 #include "multiplayer.hpp"
00030 #include "multiplayer_ui.hpp"
00031 #include "sound.hpp"
00032 #include "replay.hpp"
00033 #include "wml_separators.hpp"
00034 #include "formula_string_utils.hpp"
00035 
00036 static lg::log_domain log_engine("engine");
00037 #define LOG_NG LOG_STREAM(info, log_engine)
00038 #define ERR_NG LOG_STREAM(err, log_engine)
00039 
00040 static lg::log_domain log_config("config");
00041 #define ERR_CF LOG_STREAM(err, log_config)
00042 
00043 static lg::log_domain log_network("network");
00044 #define LOG_NW LOG_STREAM(info, log_network)
00045 #define ERR_NW LOG_STREAM(err, log_network)
00046 
00047 namespace {
00048 
00049     /** The maximum number of messages in the chat history. */
00050     const size_t max_messages = 256;
00051 
00052     class user_menu_style : public gui::menu::imgsel_style {
00053     public:
00054         user_menu_style() : gui::menu::imgsel_style("misc/selection", false,
00055                                            0x000000, 0x4a4440, 0x999999,
00056                                            0.0, 0.2, 0.2),
00057                                            item_size_(empty_rect)
00058         {}
00059         virtual void init();
00060         virtual SDL_Rect item_size(const std::string& /*item*/) const { return item_size_; }
00061         void set_width(const int width) { item_size_.w = width; }
00062     private:
00063         SDL_Rect item_size_;
00064     };
00065 
00066     void user_menu_style::init()
00067     {
00068         imgsel_style::init();
00069         item_size_.h = font::get_max_height(font_size_);
00070         scale_images(-1, item_size_.h);
00071         item_size_.h += 2 * thickness_;
00072     }
00073 
00074     user_menu_style umenu_style;
00075 
00076 } // anon namespace
00077 
00078 namespace mp {
00079 
00080 void check_response(network::connection res, const config& data)
00081 {
00082     if(!res) {
00083         throw network::error(_("Connection timed out"));
00084     }
00085 
00086     if (const config &err = data.child("error")) {
00087         throw network::error(err["message"]);
00088     }
00089 }
00090 
00091 void level_to_gamestate(config& level, game_state& state)
00092 {
00093     //any replay data is only temporary and should be removed from
00094     //the level data in case we want to save the game later
00095     const config &replay_data = level.child("replay");
00096     config replay_data_store;
00097     if (replay_data) {
00098         replay_data_store = replay_data;
00099         LOG_NW << "setting replay\n";
00100         state.replay_data = replay_data;
00101         recorder = replay(replay_data_store);
00102         if(!recorder.empty()) {
00103             recorder.set_skip(false);
00104             recorder.set_to_end();
00105         }
00106     }
00107 
00108     //set random
00109     const std::string seed = level["random_seed"];
00110     if(! seed.empty()) {
00111         const unsigned calls = lexical_cast_default<unsigned>(level["random_calls"]);
00112         state.rng().seed_random(lexical_cast<int>(seed), calls);
00113     } else {
00114         ERR_NG << "No random seed found, random "
00115             "events will probably be out of sync.\n";
00116     }
00117 
00118     //adds the starting pos to the level
00119     if (!level.child("replay_start")) {
00120         level.add_child("replay_start", level);
00121         level.child("replay_start").remove_child("multiplayer", 0);
00122     }
00123     //this is important, if it does not happen, the starting position is missing and
00124     //will be drawn from the snapshot instead (which is not what we want since we have
00125     //all needed information here already)
00126     state.starting_pos = level.child("replay_start");
00127 
00128     level["campaign_type"] = "multiplayer";
00129     state.classification().campaign_type = "multiplayer";
00130     state.classification().completion = level["completion"].str();
00131     state.classification().version = level["version"].str();
00132 
00133     if (const config &vars = level.child("variables")) {
00134         state.set_variables(vars);
00135     }
00136     state.set_menu_items(level.child_range("menu_item"));
00137     state.mp_settings().set_from_config(level);
00138 
00139     //Check whether it is a save-game by looking for snapshot data
00140     const config &snapshot = level.child("snapshot");
00141     const bool saved_game = snapshot && snapshot.child("side");
00142 
00143     //It might be a MP campaign start-of-scenario save
00144     //In this case, it's not entirely a new game, but not a save, either
00145     //Check whether it is no savegame and the starting_pos contains [player] information
00146     bool start_of_scenario = !saved_game && state.starting_pos.child("player");
00147 
00148     //If we start a fresh game, there won't be any snapshot information. If however this
00149     //is a savegame, we got a valid snapshot here.
00150     if (saved_game) {
00151         state.snapshot = snapshot;
00152         if (const config &v = snapshot.child("variables")) {
00153             state.set_variables(v);
00154         }
00155         state.set_menu_items(snapshot.child_range("menu_item"));
00156     }
00157 
00158     //In any type of reload(normal save or start-of-scenario) the players could have
00159     //changed and need to be replaced
00160     if(saved_game || start_of_scenario){
00161         config::child_itors saved_sides = saved_game ?
00162             state.snapshot.child_range("side") :
00163             state.starting_pos.child_range("side");
00164         config::const_child_itors level_sides = level.child_range("side");
00165 
00166         foreach (config &side, saved_sides)
00167         {
00168             foreach (const config &lside, level_sides)
00169             {
00170                 if (side["side"] == lside["side"] &&
00171                         (side["current_player"] != lside["current_player"] ||
00172                          side["controller"] != lside["controller"]))
00173                 {
00174                     side["current_player"] = lside["current_player"];
00175                     side["id"] = lside["id"];
00176                     side["save_id"] = lside["save_id"];
00177                     side["controller"] = lside["controller"];
00178                     break;
00179                 }
00180             }
00181         }
00182     }
00183     if(state.get_variables().empty()) {
00184         LOG_NG << "No variables were found for the game_state." << std::endl;
00185     } else {
00186         LOG_NG << "Variables found and loaded into game_state:" << std::endl;
00187         LOG_NG << state.get_variables();
00188     }
00189 }
00190 
00191 std::string get_color_string(int id)
00192 {
00193     std::string prefix = team::get_side_highlight(id);
00194     std::map<std::string, t_string>::iterator name = game_config::team_rgb_name.find(str_cast(id + 1));
00195     if(name != game_config::team_rgb_name.end()){
00196         return prefix + name->second;
00197     }else{
00198         return prefix + _("Invalid Color");
00199     }
00200 }
00201 
00202 chat::chat() :
00203     message_history_(),
00204     last_update_()
00205 {
00206 }
00207 
00208 void chat::add_message(const time_t& time, const std::string& user,
00209         const std::string& message)
00210 {
00211     message_history_.push_back(msg(time, user, message));
00212 
00213     while (message_history_.size() > max_messages) {
00214         message_history_.pop_front();
00215 
00216         if (last_update_ > 0)
00217             last_update_--;
00218     }
00219 }
00220 
00221 void chat::init_textbox(gui::textbox& textbox)
00222 {
00223     for(msg_hist::const_iterator itor = message_history_.begin();
00224             itor != message_history_.end(); ++itor) {
00225         textbox.append_text(format_message(*itor), true, color_message(*itor));
00226     }
00227 
00228     last_update_ = message_history_.size();
00229 }
00230 
00231 void chat::update_textbox(gui::textbox& textbox)
00232 {
00233     for(msg_hist::const_iterator itor = message_history_.begin() + last_update_;
00234             itor != message_history_.end(); ++itor) {
00235         textbox.append_text(format_message(*itor), true, color_message(*itor));
00236     }
00237 
00238     last_update_ = message_history_.size();
00239 }
00240 
00241 std::string chat::format_message(const msg& message)
00242 {
00243     std::string msg_text = message.message;
00244     if(message.user == "server"
00245     || message.user.substr(0,29) == "whisper: server message from ") {
00246         std::string::const_iterator after_markup =
00247             font::parse_markup(message.message.begin(), message.message.end(), NULL, NULL, NULL);
00248 
00249         msg_text = std::string(after_markup,message.message.end());
00250     }
00251     if(message.message.substr(0,3) == "/me") {
00252         return preferences::get_chat_timestamp(message.time) + "<" + message.user
00253                 + msg_text.substr(3) + ">\n";
00254     } else {
00255         return preferences::get_chat_timestamp(message.time) + "<" + message.user
00256                 + "> " + msg_text + "\n";
00257     }
00258 }
00259 
00260 SDL_Color chat::color_message(const msg& message) {
00261     SDL_Color c = font::NORMAL_COLOR;
00262     // Normal users are not allowed to color their messages
00263     if(message.user == "server"
00264     || message.user.substr(0,29) == "whisper: server message from ") {
00265         font::parse_markup(message.message.begin(), message.message.end(), NULL, &c, NULL);
00266     // Highlight private messages too
00267     } else if(message.user.substr(0,8) == "whisper:") {
00268         c = font::LABEL_COLOR;
00269     }
00270     return c;
00271 }
00272 
00273 ui::ui(game_display& disp, const std::string& title, const config& cfg, chat& c, config& gamelist) :
00274     gui::widget(disp.video()),
00275     disp_(disp),
00276     initialized_(false),
00277     gamelist_initialized_(false),
00278 
00279     hotkey_handler_(&disp),
00280     disp_manager_(&disp),
00281 
00282     game_config_(cfg),
00283     chat_(c),
00284     gamelist_(gamelist),
00285 
00286     title_(disp.video(), title, font::SIZE_LARGE, font::TITLE_COLOR),
00287     entry_textbox_(disp.video(), 100),
00288     chat_textbox_(disp.video(), 100, "", false),
00289     users_menu_(disp.video(), std::vector<std::string>(), false, -1, -1, NULL, &umenu_style),
00290 
00291     user_list_(),
00292     selected_game_(""),
00293     selected_user_(""),
00294     selected_user_changed_(false),
00295 
00296     result_(CONTINUE),
00297     gamelist_refresh_(false),
00298     lobby_clock_(0)
00299 {
00300     const SDL_Rect area = create_rect(0
00301             , 0
00302             , disp.video().getx()
00303             , disp.video().gety());
00304     users_menu_.set_numeric_keypress_selection(false);
00305     set_location(area);
00306 }
00307 
00308 void ui::process_network()
00309 {
00310     config data;
00311     try {
00312         const network::connection sock = network::receive_data(data);
00313 
00314         if(sock) {
00315             process_network_data(data, sock);
00316         }
00317     } catch(network::error& e) {
00318         process_network_error(e);
00319     }
00320 
00321     //apply diffs at a set interval
00322     if(gamelist_refresh_ && SDL_GetTicks() - lobby_clock_ > game_config::lobby_refresh)
00323     {
00324         const cursor::setter cursor_setter(cursor::WAIT);
00325         gamelist_updated(false);
00326         gamelist_refresh_ = false;
00327         lobby_clock_ = SDL_GetTicks();
00328     }
00329 
00330     if (accept_connections()) {
00331         network::connection sock = network::accept_connection();
00332         if(sock) {
00333             LOG_NW << "Received connection\n";
00334 
00335             process_network_connection(sock);
00336         }
00337     }
00338 }
00339 
00340 ui::result ui::get_result()
00341 {
00342     return result_;
00343 }
00344 
00345 ui::result ui::set_result(ui::result res)
00346 {
00347     result_ = res;
00348     return res;
00349 }
00350 
00351 const int ui::xscale_base = 1024;
00352 const int ui::yscale_base =  768;
00353 
00354 int ui::xscale(int x) const
00355 {
00356     return (x * width())/ui::xscale_base;
00357 }
00358 
00359 int ui::yscale(int y) const
00360 {
00361     return (y * height())/ui::yscale_base;
00362 }
00363 
00364 SDL_Rect ui::client_area() const
00365 {
00366     SDL_Rect res;
00367 
00368     res.x = xscale(10) + 10;
00369     res.y = yscale(38) + 10;
00370     res.w = xscale(828) > 12 ? xscale(828) - 12 : 0;
00371     res.h = yscale(520) > 12 ? yscale(520) - 12 : 0;
00372 
00373     return res;
00374 }
00375 
00376 const config& ui::game_config() const
00377 {
00378     return game_config_;
00379 }
00380 
00381 void ui::draw_contents()
00382 {
00383     hide_children();
00384 
00385     surface background(image::get_image("misc/lobby.png"));
00386     background = scale_surface(background, video().getx(), video().gety());
00387     if(background == NULL)
00388         return;
00389     sdl_blit(background, NULL, video().getSurface(), NULL);
00390     update_whole_screen();
00391 
00392     hide_children(false);
00393 }
00394 
00395 void ui::set_location(const SDL_Rect& rect)
00396 {
00397     hide_children();
00398     widget::set_location(rect);
00399     layout_children(rect);
00400     if(!initialized_) {
00401         chat_textbox_.set_wrap(true);
00402         chat_.init_textbox(chat_textbox_);
00403         initialized_ = true;
00404     }
00405     hide_children(false);
00406 }
00407 
00408 void ui::process_event()
00409 {
00410 }
00411 
00412 void ui::handle_event(const SDL_Event& event)
00413 {
00414     if(event.type == SDL_KEYDOWN) {
00415         handle_key_event(event.key);
00416     }
00417     if(users_menu_.double_clicked()) {
00418         std::string usr_text = user_list_[users_menu_.selection()];
00419         Uint32 show_time = SDL_GetTicks();
00420 
00421         // Hack: for some reason the help string stays visible for ever
00422         /** @todo find out why the help string stays visible and fix it */
00423         disp().video().clear_all_help_strings();
00424 
00425         gui2::tmp_cmd_wrapper dlg(_("Selected user: ") + usr_text);
00426         dlg.show(disp().video());
00427 
00428         std::stringstream msg;
00429         switch(dlg.get_retval()) {
00430             case -1:
00431                 if(!dlg.message().empty()) msg << "/msg " << usr_text << ' ' << dlg.message();
00432                 break;
00433             case 1:
00434                 msg << "/friend " << usr_text;
00435                 break;
00436             case 2:
00437                 msg << "/ignore " << usr_text;
00438                 break;
00439             case 3:
00440                 msg << "/remove " << usr_text;
00441                 break;
00442             case 4:
00443                 msg << "/query status " << usr_text;
00444                 break;
00445             case 5:
00446                 msg << "/query kick " << usr_text;
00447                 if(!dlg.reason().empty()) msg << ' ' << dlg.reason();
00448                 break;
00449             case 6:
00450                 msg << "/query kban " << usr_text;
00451                 if(!dlg.time().empty()) msg << ' ' << dlg.time();
00452                 if(!dlg.reason().empty()) msg << ' ' << dlg.reason();
00453         }
00454 
00455         chat_handler::do_speak(msg.str());
00456 
00457         if(show_time + 60000 < SDL_GetTicks()) {
00458             //if the dialog has been open for a long time, refresh the lobby
00459             config request;
00460             request.add_child("refresh_lobby");
00461             network::send_data(request, 0);
00462         }
00463     }
00464     if(users_menu_.selection() > 0 // -1 indicates an invalid selection
00465             && selected_user_ != user_list_[users_menu_.selection()]) {
00466         selected_user_ = user_list_[users_menu_.selection()];
00467         selected_user_changed_ = true;
00468     }
00469 }
00470 
00471 void ui::add_chat_message(const time_t& time, const std::string& speaker, int /*side*/, const std::string& message, events::chat_handler::MESSAGE_TYPE /*type*/)
00472 {
00473     chat_.add_message(time, speaker, message);
00474     chat_.update_textbox(chat_textbox_);
00475 }
00476 
00477 void ui::send_chat_message(const std::string& message, bool /*allies_only*/)
00478 {
00479     config data, msg;
00480     msg["message"] = message;
00481     msg["sender"] = preferences::login();
00482     data.add_child("message", msg);
00483 
00484     add_chat_message(time(NULL), preferences::login(),0, message);  //local echo
00485     network::send_data(data, 0);
00486 }
00487 
00488 void ui::handle_key_event(const SDL_KeyboardEvent& event)
00489 {
00490     //On enter, adds the current chat message to the chat textbox.
00491     if((event.keysym.sym == SDLK_RETURN || event.keysym.sym == SDLK_KP_ENTER) && !entry_textbox_.text().empty()) {
00492 
00493         chat_handler::do_speak(entry_textbox_.text());
00494         entry_textbox_.clear();
00495     // nick tab-completion
00496     } else if(event.keysym.sym == SDLK_TAB ) {
00497         std::string text = entry_textbox_.text();
00498         std::vector<std::string> matches = user_list_;
00499         // Exclude own nick from tab-completion.
00500         matches.erase(std::remove(matches.begin(), matches.end(),
00501                 preferences::login()), matches.end());
00502         const bool line_start = utils::word_completion(text, matches);
00503 
00504         if (matches.empty()) return;
00505 
00506         if (matches.size() == 1) {
00507             text.append(line_start ? ": " : " ");
00508         } else {
00509             std::string completion_list = utils::join(matches, " ");
00510             chat_.add_message(time(NULL), "", completion_list);
00511             chat_.update_textbox(chat_textbox_);
00512         }
00513         entry_textbox_.set_text(text);
00514     }
00515 }
00516 
00517 void ui::process_message(const config& msg, const bool whisper) {
00518     const std::string& sender = msg["sender"];
00519     const std::string& message = msg["message"];
00520     std::string room = msg["room"];
00521     if (!preferences::parse_should_show_lobby_join(sender, message)) return;
00522     if (preferences::is_ignored(sender)) return;
00523 
00524     preferences::parse_admin_authentication(sender, message);
00525 
00526     if (whisper || utils::word_match(message, preferences::login())) {
00527         sound::play_UI_sound(game_config::sounds::receive_message_highlight);
00528     } else if (preferences::is_friend(sender)) {
00529         sound::play_UI_sound(game_config::sounds::receive_message_friend);
00530     } else if (sender == "server") {
00531         sound::play_UI_sound(game_config::sounds::receive_message_server);
00532     } else {
00533         // too annoying and probably not any helpful
00534         //sound::play_UI_sound(game_config::sounds::receive_message);
00535     }
00536 
00537     std::string prefix;
00538 
00539     if(whisper) {
00540         utils::string_map symbols;
00541         symbols["sender"] = msg["sender"].str();
00542         prefix = VGETTEXT("whisper: $sender", symbols);
00543     }
00544     else {
00545         prefix = msg["sender"].str();
00546     }
00547 
00548     if (!room.empty()) room = room + ": ";
00549 
00550     chat_.add_message(time(NULL), room + prefix, msg["message"]);
00551     chat_.update_textbox(chat_textbox_);
00552 }
00553 
00554 void ui::process_network_data(const config& data, const network::connection /*sock*/)
00555 {
00556     if (const config &c = data.child("error")) {
00557         throw network::error(c["message"]);
00558     } else if (const config &c = data.child("message")) {
00559         process_message(c);
00560     } else if (const config &c = data.child("whisper")) {
00561         process_message(c, true);
00562     } else if(data.child("gamelist")) {
00563         const cursor::setter cursor_setter(cursor::WAIT);
00564         gamelist_initialized_ = true;
00565         gamelist_ = data;
00566         gamelist_updated(false);
00567         gamelist_refresh_ = false;
00568         lobby_clock_ = SDL_GetTicks();
00569     } else if (const config &c = data.child("gamelist_diff")) {
00570         if(gamelist_initialized_) {
00571             try {
00572                 gamelist_.apply_diff(c);
00573             } catch(config::error& e) {
00574                 ERR_CF << "Error while applying the gamelist diff: '"
00575                     << e.message << "' Getting a new gamelist.\n";
00576                 network::send_data(config("refresh_lobby"), 0);
00577             }
00578             gamelist_refresh_ = true;
00579         }
00580     } else if (const config &c = data.child("room_join")) {
00581         if (c["player"] == preferences::login()) {
00582             chat_.add_message(time(NULL), "server",
00583                 "You have joined the room '" + c["room"].str() + "'");
00584         } else {
00585             chat_.add_message(time(NULL), "server",
00586                 c["player"].str() + " has joined the room '" + c["room"].str() + "'");
00587         }
00588         chat_.update_textbox(chat_textbox_);
00589     } else if (const config &c = data.child("room_part")) {
00590         if (c["player"] == preferences::login()) {
00591             chat_.add_message(time(NULL), "server",
00592                 "You have left the room '" + c["room"].str() + "'");
00593         } else {
00594             chat_.add_message(time(NULL), "server",
00595                 c["player"].str() + " has left the room '" + c["room"].str() + "'");
00596         }
00597         chat_.update_textbox(chat_textbox_);
00598     } else if (const config &c = data.child("room_query_response")) {
00599         if (const config &ms = c.child("members")) {
00600             std::stringstream ss;
00601             ss << "Room " << c["room"].str() << " members: ";
00602             foreach (const config& m, ms.child_range("member")) {
00603                 ss << m["name"] << " ";
00604             }
00605             chat_.add_message(time(NULL), "server", ss.str());
00606             chat_.update_textbox(chat_textbox_);
00607         }
00608         if (const config &rs = c.child("rooms")) {
00609             std::stringstream ss;
00610             ss << "Rooms: ";
00611             foreach (const config& r, rs.child_range("room")) {
00612                 ss << r["name"].str() << "(" << r["size"].str() << ") ";
00613             }
00614             chat_.add_message(time(NULL), "server", ss.str());
00615             chat_.update_textbox(chat_textbox_);
00616         }
00617     }
00618 }
00619 
00620 void ui::process_network_error(network::error& error)
00621 {
00622     ERR_NW << "Caught networking error: " << error.message << "\n";
00623 
00624     // Default behaviour is to re-throw the error. May be overridden.
00625     throw error;
00626 }
00627 
00628 void ui::process_network_connection(const network::connection /*sock*/)
00629 {
00630     LOG_NW << "Caught network connection.\n";
00631 }
00632 
00633 void ui::hide_children(bool hide)
00634 {
00635     title_.hide(hide);
00636     chat_textbox_.hide(hide);
00637     entry_textbox_.hide(hide);
00638     users_menu_.hide(hide);
00639 }
00640 
00641 void ui::layout_children(const SDL_Rect& /*rect*/)
00642 {
00643     title_.set_location(xscale(12) + 8, yscale(38) + 8);
00644     umenu_style.set_width(xscale(159));
00645     users_menu_.set_width(xscale(159));
00646     users_menu_.set_max_width(xscale(159));
00647     users_menu_.set_location(xscale(856), yscale(42));
00648     users_menu_.set_height(yscale(715));
00649     users_menu_.set_max_height(yscale(715));
00650     chat_textbox_.set_location(xscale(11) + 4, yscale(573) + 4);
00651     chat_textbox_.set_measurements(xscale(833) - 8, yscale(143) - 8);
00652     entry_textbox_.set_location(xscale(11) + 4, yscale(732));
00653     entry_textbox_.set_width(xscale(833) - 8);
00654 }
00655 
00656 bool ui::user_info::operator> (const user_info& b) const {
00657     user_info const& a = *this;
00658 
00659     // ME always on top
00660     if (a.relation == ME) {
00661         return true;
00662     }
00663     if (b.relation == ME) {
00664         return false;
00665     }
00666 
00667     // friends next, sorted by location
00668     if ((a.relation == FRIEND) && (b.relation == FRIEND)) {
00669         if (a.state != b.state) {
00670             return a.state < b.state;
00671         }
00672         return a.name < b.name;
00673     }
00674     if (a.relation == FRIEND) {
00675         return true;
00676     }
00677     if (b.relation == FRIEND) {
00678         return false;
00679     }
00680 
00681     // players in the selected game next, sorted by relation (friends/neutral/ignored)
00682     if ((a.state == SEL_GAME) && (b.state == SEL_GAME)) {
00683         if (a.relation != b.relation) {
00684             return a.relation < b.relation;
00685         }
00686         return a.name < b.name;
00687     }
00688     if (a.state == SEL_GAME) {
00689         return true;
00690     }
00691     if (b.state == SEL_GAME) {
00692         return false;
00693     }
00694 
00695     // all others grouped by relation
00696     if (a.relation != b.relation) {
00697         return a.relation < b.relation;
00698     }
00699     if (a.state != b.state) {
00700         return a.state < b.state;
00701     }
00702     return a.name < b.name;
00703 }
00704 
00705 void ui::gamelist_updated(bool silent)
00706 {
00707     std::list<user_info> u_list;
00708 
00709     foreach (const config &user, gamelist_.child_range("user"))
00710     {
00711         user_info u_elem;
00712         u_elem.name = user["name"].str();
00713         u_elem.state = user["available"].to_bool(true) ? LOBBY : GAME;
00714         u_elem.registered = user["registered"].to_bool();
00715         u_elem.game_id = user["game_id"].str();
00716         u_elem.location = user["location"].str();
00717         if (!u_elem.game_id.empty() && u_elem.game_id == selected_game_) {
00718             u_elem.state = SEL_GAME;
00719         }
00720         if (u_elem.name == preferences::login()) {
00721             u_elem.relation = ME;
00722         } else if (preferences::is_ignored(u_elem.name)) {
00723             u_elem.relation = IGNORED;
00724         } else if (preferences::is_friend(u_elem.name)) {
00725             u_elem.relation = FRIEND;
00726         } else {
00727             u_elem.relation = NEUTRAL;
00728         }
00729         u_list.push_back(u_elem);
00730     }
00731 
00732     if (preferences::sort_list()) {
00733         u_list.sort(std::greater<user_info>());
00734     }
00735 
00736     // can't use the bold tag here until the menu code
00737     // calculates a correct ellipsis for it
00738     const std::string lobby_color_tag   = "";
00739     const std::string ingame_color_tag  = "#";
00740     const std::string selgame_color_tag = "<0,191,255>";
00741 
00742     // for now I just disregard the above till I know something better,
00743     // it works for me anyways
00744     const std::string registered_user_tag = "~";
00745 
00746     std::string const imgpre = IMAGE_PREFIX + std::string("misc/status-");
00747     std::vector<std::string> user_strings;
00748     std::vector<std::string> menu_strings;
00749 
00750     std::list<user_info>::const_iterator u_itor = u_list.begin();
00751     while (u_itor != u_list.end()) {
00752         const std::string name_str = u_itor->name +
00753                 ((u_itor->state == LOBBY) ? "" : " (" + u_itor->location + ")");
00754         std::string img_str = "";
00755         std::string color_str = "";
00756         std::string reg_str = "";
00757         switch (u_itor->state) {
00758             case LOBBY:    color_str = lobby_color_tag;   break;
00759             case GAME:     color_str = ingame_color_tag;  break;
00760             case SEL_GAME: color_str = selgame_color_tag; break;
00761         }
00762         if (preferences::iconize_list()) {
00763             switch (u_itor->relation) {
00764                 case NEUTRAL: img_str = imgpre + "neutral.png" + IMG_TEXT_SEPARATOR; break;
00765                 case IGNORED: img_str = imgpre + "ignore.png"  + IMG_TEXT_SEPARATOR; break;
00766                 case FRIEND:  img_str = imgpre + "friend.png"  + IMG_TEXT_SEPARATOR; break;
00767                 case ME:      img_str = imgpre + "self.png"    + IMG_TEXT_SEPARATOR; break;
00768             }
00769         }
00770         reg_str = u_itor->registered ? registered_user_tag : "";
00771         user_strings.push_back(u_itor->name);
00772         menu_strings.push_back(img_str + reg_str + color_str + name_str + HELP_STRING_SEPARATOR + name_str);
00773         ++u_itor;
00774     }
00775 
00776     set_user_list(user_strings, silent);
00777     set_user_menu_items(menu_strings);
00778 }
00779 
00780 void ui::set_selected_game(const std::string& game_id)
00781 {
00782     // reposition the player list to show the players in the selected game
00783     if (preferences::sort_list() && (selected_game_ != game_id)) {
00784         users_menu_.move_selection(0);
00785     }
00786     selected_game_ = game_id;
00787 }
00788 
00789 void ui::set_user_menu_items(const std::vector<std::string>& list)
00790 {
00791     users_menu_.set_items(list,true,true);
00792 
00793     // Try to keep selected player
00794     std::vector<std::string>::const_iterator i =
00795             std::find(user_list_.begin(), user_list_.end(), selected_user_);
00796     if(i != user_list_.end()) {
00797         users_menu_.move_selection_keeping_viewport(i - user_list_.begin());
00798     }
00799 }
00800 
00801 void ui::set_user_list(const std::vector<std::string>& list, bool silent)
00802 {
00803     if(!silent) {
00804         if(list.size() < user_list_.size()) {
00805             sound::play_UI_sound(game_config::sounds::user_leave);
00806         } else if(list.size() > user_list_.size()) {
00807             sound::play_UI_sound(game_config::sounds::user_arrive);
00808         }
00809     }
00810 
00811     user_list_ = list;
00812 }
00813 
00814 std::string ui::get_selected_user_game()
00815 {
00816     const config &u = gamelist_.find_child("user", "name", selected_user_);
00817     if (u) return u["game_id"];
00818     return std::string();
00819 }
00820 
00821 void ui::append_to_title(const std::string& text) {
00822     title_.set_text(title_.get_text() + text);
00823 }
00824 
00825 const gui::label& ui::title() const
00826 {
00827     return title_;
00828 }
00829 
00830 int find_suitable_faction(faction_list const &fl, const config &cfg)
00831 {
00832     std::vector<std::string> find;
00833     std::string search_field;
00834     if (const config::attribute_value *f = cfg.get("faction")) {
00835         // Choose based on faction.
00836         find.push_back(f->str());
00837         search_field = "id";
00838     } else if (cfg["faction_from_recruit"].to_bool()) {
00839         // Choose based on recruit.
00840         find = utils::split(cfg["recruit"]);
00841         search_field = "recruit";
00842     } else if (const config::attribute_value *l = cfg.get("leader")) {
00843         // Choose based on leader.
00844         find.push_back(*l);
00845         search_field = "leader";
00846     } else {
00847         return -1;
00848     }
00849 
00850     int res = -1, index = 0, best_score = 0;
00851     foreach (const config *faction, fl)
00852     {
00853         int faction_score = 0;
00854         std::vector<std::string> recruit = utils::split((*faction)[search_field]);
00855         foreach (const std::string &search, find) {
00856             foreach (const std::string &r, recruit) {
00857                 if (r == search) {
00858                     ++faction_score;
00859                     break;
00860                 }
00861             }
00862         }
00863         if (faction_score > best_score) {
00864             best_score = faction_score;
00865             res = index;
00866         }
00867         ++index;
00868     }
00869     return res;
00870 }
00871 
00872 }// namespace mp
 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