multiplayer_ui.cpp

Go to the documentation of this file.
00001 /* $Id: multiplayer_ui.cpp 25997 2008-04-22 11:21:55Z soliton $ */
00002 /*
00003    Copyright (C) 2005 - 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 #include "global.hpp"
00016 
00017 #include "config.hpp"
00018 #include "construct_dialog.hpp"
00019 #include "game_display.hpp"
00020 #include "font.hpp"
00021 #include "marked-up_text.hpp"
00022 #include "gettext.hpp"
00023 #include "game_config.hpp"
00024 #include "image.hpp"
00025 #include "log.hpp"
00026 #include "multiplayer_ui.hpp"
00027 #include "network.hpp"
00028 #include "sound.hpp"
00029 #include "video.hpp"
00030 #include "replay.hpp"
00031 #include "wml_separators.hpp"
00032 
00033 #define LOG_NG LOG_STREAM(info, engine)
00034 #define ERR_NG LOG_STREAM(err, engine)
00035 #define ERR_CF LOG_STREAM(err, config)
00036 #define DBG_NW LOG_STREAM(debug, network)
00037 #define LOG_NW LOG_STREAM(info, network)
00038 #define ERR_NW LOG_STREAM(err, network)
00039 
00040 namespace {
00041 
00042     class user_menu_style : public gui::menu::imgsel_style {
00043     public:
00044         user_menu_style() : gui::menu::imgsel_style("misc/selection", false,
00045                                            0x000000, 0x4a4440, 0x999999,
00046                                            0.0, 0.2, 0.2),
00047                                            item_size_(empty_rect)
00048         {}
00049         virtual void init();
00050         virtual SDL_Rect item_size(const std::string& /*item*/) const { return item_size_; }
00051         void set_width(const int width) { item_size_.w = width; }
00052     private:
00053         SDL_Rect item_size_;
00054     };
00055 
00056     void user_menu_style::init()
00057     {
00058         imgsel_style::init();
00059         item_size_.h = font::get_max_height(font_size_);
00060         scale_images(-1, item_size_.h);
00061         item_size_.h += 2 * thickness_;
00062     }
00063 
00064     user_menu_style umenu_style;
00065 
00066 } // anon namespace
00067 
00068 namespace mp {
00069 
00070 void check_response(network::connection res, const config& data)
00071 {
00072     if(!res) {
00073         throw network::error(_("Connection timed out"));
00074     }
00075 
00076     const config* err = data.child("error");
00077     if(err != NULL) {
00078         throw network::error((*err)["message"]);
00079     }
00080 }
00081 
00082 void level_to_gamestate(config& level, game_state& state, bool saved_game)
00083 {
00084     //any replay data is only temporary and should be removed from
00085     //the level data in case we want to save the game later
00086     config * const replay_data = level.child("replay");
00087     config replay_data_store;
00088     if(replay_data != NULL) {
00089         replay_data_store = *replay_data;
00090         LOG_NW << "setting replay\n";
00091         state.replay_data = *replay_data;
00092         recorder = replay(replay_data_store);
00093         if(!recorder.empty()) {
00094             recorder.set_skip(false);
00095             recorder.set_to_end();
00096         }
00097     }
00098 
00099     //set random
00100     const std::string seed = level["random_seed"];
00101     if(! seed.empty()) {
00102         const unsigned calls = lexical_cast_default<unsigned>(level["random_calls"]);
00103         state.rng().seed_random(lexical_cast<int>(seed), calls);
00104     } else {
00105         ERR_NG << "No random seed found, random "
00106             "events will probably be out of sync.\n";
00107     }
00108 
00109     //adds the starting pos to the level
00110     if(level.child("replay_start") == NULL){
00111         level.add_child("replay_start", level);
00112     }
00113     //this is important, if it does not happen, the starting position is missing and
00114     //will be drawn from the snapshot instead (which is not what we want since we have
00115     //all needed information here already)
00116     state.starting_pos = *(level.child("replay_start"));
00117 
00118     level["campaign_type"] = "multiplayer";
00119     state.campaign_type = "multiplayer";
00120     state.version = level["version"];
00121 
00122     const config* const vars = level.child("variables");
00123     if(vars != NULL) {
00124         state.set_variables(*vars);
00125     }
00126     state.set_menu_items(level.get_children("menu_item"));
00127 
00128     //If we start a fresh game, there won't be any snapshot information. If however this
00129     //is a savegame, we got a valid snapshot here.
00130     if (saved_game){
00131         state.snapshot = *(level.child("snapshot"));
00132         if (state.snapshot.child("variables") != NULL){
00133             state.set_variables(*state.snapshot.child("variables"));
00134         }
00135         state.set_menu_items(state.snapshot.get_children("menu_item"));
00136 
00137         //We also need to take into account, that the reload could take place with different players.
00138         //If so, they are substituted now.
00139         const config::child_list& snapshot_sides = state.snapshot.get_children("side");
00140         const config::child_list& level_sides = level.get_children("side");
00141         for(config::child_list::const_iterator side = snapshot_sides.begin(); side != snapshot_sides.end(); ++side) {
00142             for(config::child_list::const_iterator lside = level_sides.begin(); lside != level_sides.end(); ++lside) {
00143                 if ( ((**side)["side"] == (**lside)["side"])
00144                     && ((**side)["current_player"] != (**lside)["current_player"]) ){
00145                     (**side)["current_player"] = (**lside)["current_player"];
00146                     (**side)["id"] = (**lside)["id"];
00147                     (**side)["save_id"] = (**lside)["save_id"];
00148                     (**side)["controller"] = (**lside)["controller"];
00149                     break;
00150                 }
00151             }
00152         }
00153     }
00154     if(state.get_variables().empty()) {
00155         LOG_NG << "No variables were found for the game_state." << std::endl;
00156     } else {
00157         LOG_NG << "Variables found and loaded into game_state:" << std::endl;
00158         LOG_NG << state.get_variables();
00159     }
00160 }
00161 
00162 std::string get_colour_string(int id)
00163 {
00164     std::string prefix = team::get_side_highlight(id);
00165     std::stringstream side_id;
00166     side_id << (id + 1);
00167     std::map<std::string, t_string>::iterator name = game_config::team_rgb_name.find(side_id.str());
00168     if(name != game_config::team_rgb_name.end()){
00169         return prefix + name->second;
00170     }else{
00171         return prefix + _("Invalid Color");
00172     }
00173 }
00174 
00175 chat::chat()
00176 {
00177 }
00178 
00179 void chat::add_message(const time_t& time, const std::string& user,
00180         const std::string& message)
00181 {
00182     message_history_.push_back(msg(time, user, message));
00183 
00184     while (message_history_.size() > 1024) {
00185         message_history_.pop_front();
00186 
00187         if (last_update_ > 0)
00188             last_update_--;
00189     }
00190 }
00191 
00192 void chat::init_textbox(gui::textbox& textbox)
00193 {
00194     std::string s;
00195 
00196     for(msg_hist::const_iterator itor = message_history_.begin();
00197             itor != message_history_.end(); ++itor) {
00198         s.append(format_message(*itor));
00199     }
00200 
00201     textbox.set_text(s);
00202     last_update_ = message_history_.size();
00203     textbox.scroll_to_bottom();
00204 }
00205 
00206 void chat::update_textbox(gui::textbox& textbox)
00207 {
00208     std::string s;
00209 
00210     for(msg_hist::const_iterator itor = message_history_.begin() + last_update_;
00211             itor != message_history_.end(); ++itor) {
00212         s.append(format_message(*itor));
00213     }
00214 
00215     textbox.append_text(s,true);
00216 
00217     last_update_ = message_history_.size();
00218 }
00219 
00220 std::string chat::format_message(const msg& message)
00221 {
00222     if(message.message.substr(0,3) == "/me") {
00223         return preferences::get_chat_timestamp(message.time) + "<" + message.user
00224                 + message.message.substr(3) + ">\n";
00225     } else {
00226         return preferences::get_chat_timestamp(message.time) + "<" + message.user
00227                 + "> " + message.message + "\n";
00228     }
00229 }
00230 
00231 ui::ui(game_display& disp, const std::string& title, const config& cfg, chat& c, config& gamelist) :
00232     gui::widget(disp.video()),
00233     disp_(disp),
00234     initialized_(false),
00235     gamelist_initialized_(false),
00236 
00237     hotkey_handler_(&disp),
00238     disp_manager_(&disp),
00239 
00240     game_config_(cfg),
00241     chat_(c),
00242     gamelist_(gamelist),
00243 
00244 #ifdef USE_TINY_GUI
00245     title_(disp.video(), title, font::SIZE_SMALL, font::TITLE_COLOUR),
00246 #else
00247     title_(disp.video(), title, font::SIZE_LARGE, font::TITLE_COLOUR),
00248     entry_textbox_(disp.video(), 100),
00249 #endif
00250     chat_textbox_(disp.video(), 100, "", false),
00251     users_menu_(disp.video(), std::vector<std::string>(), false, -1, -1, NULL, &umenu_style),
00252 
00253     selected_game_(""),
00254 
00255     result_(CONTINUE),
00256     gamelist_refresh_(false),
00257     lobby_clock_(0)
00258 {
00259     const SDL_Rect area = { 0, 0, disp.video().getx(), disp.video().gety() };
00260     users_menu_.set_numeric_keypress_selection(false);
00261     set_location(area);
00262 }
00263 
00264 void ui::process_network()
00265 {
00266     config data;
00267     try {
00268         const network::connection sock = network::receive_data(data);
00269 
00270         if(sock) {
00271             process_network_data(data, sock);
00272         }
00273     } catch(network::error& e) {
00274         process_network_error(e);
00275     }
00276 
00277     //apply diffs at a set interval
00278     if(gamelist_refresh_ && SDL_GetTicks() - lobby_clock_ > game_config::lobby_refresh)
00279     {
00280         const cursor::setter cursor_setter(cursor::WAIT);
00281         gamelist_updated(false);
00282         gamelist_refresh_ = false;
00283         lobby_clock_ = SDL_GetTicks();
00284     }
00285 
00286     if (accept_connections()) {
00287         network::connection sock = network::accept_connection();
00288         if(sock) {
00289             LOG_NW << "Received connection\n";
00290 
00291             process_network_connection(sock);
00292         }
00293     }
00294 }
00295 
00296 ui::result ui::get_result()
00297 {
00298     return result_;
00299 }
00300 
00301 ui::result ui::set_result(ui::result res)
00302 {
00303     result_ = res;
00304     return res;
00305 }
00306 
00307 const int ui::xscale_base = 1024;
00308 const int ui::yscale_base =  768;
00309 
00310 int ui::xscale(int x) const
00311 {
00312     return (x * width())/ui::xscale_base;
00313 }
00314 
00315 int ui::yscale(int y) const
00316 {
00317     return (y * height())/ui::yscale_base;
00318 }
00319 
00320 SDL_Rect ui::client_area() const
00321 {
00322     SDL_Rect res;
00323 
00324     res.x = xscale(10) + 10;
00325     res.y = yscale(38) + 10;
00326     res.w = xscale(828) > 12 ? xscale(828) - 12 : 0;
00327     res.h = yscale(520) > 12 ? yscale(520) - 12 : 0;
00328 
00329     return res;
00330 }
00331 
00332 const config& ui::game_config() const
00333 {
00334     return game_config_;
00335 }
00336 
00337 void ui::draw_contents()
00338 {
00339     hide_children();
00340 
00341 #ifdef USE_TINY_GUI
00342     surface background(image::get_image("misc/lobby_tiny.png"));
00343 #else
00344     surface background(image::get_image("misc/lobby.png"));
00345 #endif
00346     background = scale_surface(background, video().getx(), video().gety());
00347     if(background == NULL)
00348         return;
00349     SDL_BlitSurface(background, NULL, video().getSurface(), NULL);
00350     update_whole_screen();
00351 
00352     hide_children(false);
00353 }
00354 
00355 void ui::set_location(const SDL_Rect& rect)
00356 {
00357     hide_children();
00358     widget::set_location(rect);
00359     layout_children(rect);
00360     if(!initialized_) {
00361         chat_textbox_.set_wrap(true);
00362         chat_.init_textbox(chat_textbox_);
00363         initialized_ = true;
00364     }
00365     hide_children(false);
00366 }
00367 
00368 void ui::process_event()
00369 {
00370 }
00371 
00372 void ui::handle_event(const SDL_Event& event)
00373 {
00374     if(event.type == SDL_KEYDOWN) {
00375         handle_key_event(event.key);
00376     }
00377     if(users_menu_.double_clicked()) {
00378         std::string usr_text = user_list_[users_menu_.selection()];
00379         std::string caption = _("Send a private message to ") + usr_text;
00380         gui::dialog d(disp(), _("Whisper"), caption, gui::OK_CANCEL);
00381         d.set_textbox( _("Message: "));
00382         Uint32 show_time = SDL_GetTicks();
00383         if (!(d.show() || d.textbox_text().empty())) {
00384             std::stringstream msg;
00385             msg << "/msg " << usr_text << ' ' << d.textbox_text();
00386             chat_handler::do_speak(msg.str());
00387         }
00388         if(show_time + 60000 < SDL_GetTicks()) {
00389             //if the dialog has been open for a long time, refresh the lobby
00390             config request;
00391             request.add_child("refresh_lobby");
00392             network::send_data(request, 0, true);
00393         }
00394     }
00395 }
00396 
00397 void ui::add_chat_message(const time_t& time, const std::string& speaker, int /*side*/, const std::string& message, game_display::MESSAGE_TYPE /*type*/)
00398 {
00399     chat_.add_message(time, speaker, message);
00400     chat_.update_textbox(chat_textbox_);
00401 }
00402 
00403 void ui::send_chat_message(const std::string& message, bool /*allies_only*/)
00404 {
00405     config data, msg;
00406     msg["message"] = message;
00407     msg["sender"] = preferences::login();
00408     data.add_child("message", msg);
00409 
00410     add_chat_message(time(NULL), preferences::login(),0, message);  //local echo
00411     network::send_data(data, 0, true);
00412 }
00413 
00414 
00415 void ui::handle_key_event(const SDL_KeyboardEvent& event)
00416 {
00417 #ifndef USE_TINY_GUI
00418     //On enter, adds the current chat message to the chat textbox.
00419     if((event.keysym.sym == SDLK_RETURN || event.keysym.sym == SDLK_KP_ENTER) && !entry_textbox_.text().empty()) {
00420 
00421         chat_handler::do_speak(entry_textbox_.text());
00422         entry_textbox_.clear();
00423     // nick tab-completion
00424     } else if(event.keysym.sym == SDLK_TAB ) {
00425         std::string text = entry_textbox_.text();
00426         std::vector<std::string> matches = user_list_;
00427         // Exclude own nick from tab-completion.
00428         matches.erase(std::remove(matches.begin(), matches.end(),
00429                 preferences::login()), matches.end());
00430         const bool line_start = utils::word_completion(text, matches);
00431 
00432         if (matches.empty()) return;
00433 
00434         if (matches.size() == 1) {
00435             text.append(line_start ? ": " : " ");
00436         } else {
00437             std::string completion_list = utils::join(matches, ' ');
00438             chat_.add_message(time(NULL), "", completion_list);
00439             chat_.update_textbox(chat_textbox_);
00440         }
00441         entry_textbox_.set_text(text);
00442     }
00443 #endif
00444 }
00445 
00446 void ui::process_message(const config& msg, const bool whisper) {
00447     const std::string& sender = msg["sender"];
00448     const std::string& message = msg["message"];
00449     if (!preferences::show_lobby_join(sender, message)) return;
00450     if (preferences::is_ignored(sender)) return;
00451 
00452     if (whisper || utils::word_match(message, preferences::login())) {
00453         sound::play_UI_sound(game_config::sounds::receive_message_highlight);
00454     } else if (preferences::is_friend(sender)) {
00455         sound::play_UI_sound(game_config::sounds::receive_message_friend);
00456     } else if (sender == "server") {
00457         sound::play_UI_sound(game_config::sounds::receive_message_server);
00458     } else {
00459         sound::play_UI_sound(game_config::sounds::receive_message);
00460     }
00461     chat_.add_message(time(NULL), (whisper ? "whisper: " : "") + msg["sender"],
00462             msg["message"]);
00463     chat_.update_textbox(chat_textbox_);
00464 }
00465 
00466 void ui::process_network_data(const config& data, const network::connection /*sock*/)
00467 {
00468     if(data.child("error")) {
00469         throw network::error((*data.child("error"))["message"]);
00470     } else if (data.child("message")) {
00471         process_message(*data.child("message"));
00472     } else if(data.child("whisper")){
00473         process_message(*data.child("whisper"), true);
00474     } else if(data.child("gamelist")) {
00475         const cursor::setter cursor_setter(cursor::WAIT);
00476         gamelist_initialized_ = true;
00477         gamelist_ = data;
00478         gamelist_updated(false);
00479         gamelist_refresh_ = false;
00480         lobby_clock_ = SDL_GetTicks();
00481     } else if(data.child("gamelist_diff")) {
00482         if(gamelist_initialized_) {
00483             try {
00484                 gamelist_.apply_diff(*data.child("gamelist_diff"));
00485             } catch(config::error& e) {
00486                 ERR_CF << "Error while applying the gamelist diff: '"
00487                     << e.message << "' Getting a new gamelist.\n";
00488                 network::send_data(config("refresh_lobby"), 0, true);
00489             }
00490             gamelist_refresh_ = true;
00491         }
00492     }
00493 }
00494 
00495 void ui::process_network_error(network::error& error)
00496 {
00497     ERR_NW << "Caught networking error: " << error.message << "\n";
00498 
00499     // Default behaviour is to re-throw the error. May be overridden.
00500     throw error;
00501 }
00502 
00503 void ui::process_network_connection(const network::connection /*sock*/)
00504 {
00505     LOG_NW << "Caught network connection.\n";
00506 }
00507 
00508 void ui::hide_children(bool hide)
00509 {
00510     title_.hide(hide);
00511     chat_textbox_.hide(hide);
00512 #ifndef USE_TINY_GUI
00513     entry_textbox_.hide(hide);
00514 #endif
00515     users_menu_.hide(hide);
00516 }
00517 
00518 void ui::layout_children(const SDL_Rect& /*rect*/)
00519 {
00520     title_.set_location(xscale(12) + 8, yscale(38) + 8);
00521     umenu_style.set_width(xscale(159));
00522     users_menu_.set_width(xscale(159));
00523     users_menu_.set_max_width(xscale(159));
00524     users_menu_.set_location(xscale(856), yscale(42));
00525     users_menu_.set_height(yscale(715));
00526     users_menu_.set_max_height(yscale(715));
00527 #ifdef USE_TINY_GUI
00528     chat_textbox_.set_location(xscale(11) + 4, yscale(625) + 4);
00529     chat_textbox_.set_measurements(xscale(833) - 8, yscale(143) - 8);
00530 
00531 #else
00532     chat_textbox_.set_location(xscale(11) + 4, yscale(573) + 4);
00533     chat_textbox_.set_measurements(xscale(833) - 8, yscale(143) - 8);
00534     entry_textbox_.set_location(xscale(11) + 4, yscale(732));
00535     entry_textbox_.set_width(xscale(833) - 8);
00536 #endif
00537 }
00538 
00539 bool ui::user_info::operator> (const user_info& b) const {
00540     user_info const& a = *this;
00541 
00542     // ME always on top
00543     if (a.relation == ME) {
00544         return true;
00545     }
00546     if (b.relation == ME) {
00547         return false;
00548     }
00549 
00550     // friends next, sorted by location
00551     if ((a.relation == FRIEND) && (b.relation == FRIEND)) {
00552         if (a.state != b.state) {
00553             return a.state < b.state;
00554         }
00555         return a.name < b.name;
00556     }
00557     if (a.relation == FRIEND) {
00558         return true;
00559     }
00560     if (b.relation == FRIEND) {
00561         return false;
00562     }
00563 
00564     // players in the selected game next, sorted by relation (friends/neutral/ignored)
00565     if ((a.state == SEL_GAME) && (b.state == SEL_GAME)) {
00566         if (a.relation != b.relation) {
00567             return a.relation < b.relation;
00568         }
00569         return a.name < b.name;
00570     }
00571     if (a.state == SEL_GAME) {
00572         return true;
00573     }
00574     if (b.state == SEL_GAME) {
00575         return false;
00576     }
00577 
00578     // all others grouped by relation
00579     if (a.relation != b.relation) {
00580         return a.relation < b.relation;
00581     }
00582     if (a.state != b.state) {
00583         return a.state < b.state;
00584     }
00585     return a.name < b.name;
00586 }
00587 
00588 void ui::gamelist_updated(bool silent)
00589 {
00590     config::child_list users = gamelist_.get_children("user");
00591     config::child_iterator user;
00592     std::list<user_info> u_list;
00593 
00594     for (user = users.begin(); user != users.end(); ++user) {
00595         user_info u_elem;
00596         u_elem.name = (**user)["name"];
00597         u_elem.game_id = "";
00598         u_elem.location = "";
00599         u_elem.state = (**user)["available"] == "no" ? GAME : LOBBY;
00600         if(!(**user)["game_id"].empty()) {
00601             u_elem.game_id = (**user)["game_id"];
00602             if (u_elem.game_id == selected_game_) {
00603                 u_elem.state = SEL_GAME;
00604             }
00605         }
00606         if(!(**user)["location"].empty()) {
00607             u_elem.location = (**user)["location"];
00608         }
00609         std::vector<std::string> friends = utils::split(preferences::get("friends"));
00610         std::vector<std::string> ignores = utils::split(preferences::get("ignores"));
00611         if (u_elem.name == preferences::login()) {
00612             u_elem.relation = ME;
00613         } else if (std::find(ignores.begin(), ignores.end(), u_elem.name) != ignores.end()) {
00614             u_elem.relation = IGNORED;
00615         } else if (std::find(friends.begin(), friends.end(), u_elem.name) != friends.end()) {
00616             u_elem.relation = FRIEND;
00617         } else {
00618             u_elem.relation = NEUTRAL;
00619         }
00620         u_list.push_back(u_elem);
00621     }
00622 
00623     if (preferences::sort_list()) {
00624         u_list.sort(std::greater<user_info>());
00625     }
00626 
00627     // can't use the bold tag here until the menu code
00628     // calculates a correct ellipsis for it
00629     const std::string lobby_color_tag   = "";
00630     const std::string ingame_color_tag  = "#";
00631     const std::string selgame_color_tag = "<0,191,255>";
00632 
00633     std::string const imgpre = IMAGE_PREFIX + std::string("misc/status-");
00634     std::vector<std::string> user_strings;
00635     std::vector<std::string> menu_strings;
00636 
00637     std::list<user_info>::const_iterator u_itor = u_list.begin();
00638     while (u_itor != u_list.end()) {
00639         const std::string name_str = u_itor->name +
00640                 ((u_itor->state == LOBBY) ? "" : " (" + u_itor->location + ")");
00641         std::string img_str = "";
00642         std::string color_str = "";
00643         switch (u_itor->state) {
00644             case LOBBY:    color_str = lobby_color_tag;   break;
00645             case GAME:     color_str = ingame_color_tag;  break;
00646             case SEL_GAME: color_str = selgame_color_tag; break;
00647         }
00648         if (preferences::iconize_list()) {
00649             switch (u_itor->relation) {
00650                 case NEUTRAL: img_str = imgpre + "neutral.png" + IMG_TEXT_SEPARATOR; break;
00651                 case IGNORED: img_str = imgpre + "ignore.png"  + IMG_TEXT_SEPARATOR; break;
00652                 case FRIEND:  img_str = imgpre + "friend.png"  + IMG_TEXT_SEPARATOR; break;
00653                 case ME:      img_str = imgpre + "self.png"    + IMG_TEXT_SEPARATOR; break;
00654             }
00655         }
00656         user_strings.push_back(u_itor->name);
00657         menu_strings.push_back(img_str + color_str + name_str + HELP_STRING_SEPARATOR + name_str);
00658         u_itor++;
00659     }
00660 
00661     set_user_list(user_strings, silent);
00662     set_user_menu_items(menu_strings);
00663 }
00664 
00665 void ui::set_selected_game(const std::string game_id)
00666 {
00667     // reposition the player list to show the players in the selected game
00668     if (preferences::sort_list() && (selected_game_ != game_id)) {
00669         users_menu_.move_selection(0);
00670     }
00671     selected_game_ = game_id;
00672 }
00673 
00674 void ui::set_user_menu_items(const std::vector<std::string>& list)
00675 {
00676     users_menu_.set_items(list,true,true);
00677 }
00678 
00679 void ui::set_user_list(const std::vector<std::string>& list, bool silent)
00680 {
00681     if(!silent) {
00682         if(list.size() < user_list_.size()) {
00683             sound::play_UI_sound(game_config::sounds::user_leave);
00684         } else if(list.size() > user_list_.size()) {
00685             sound::play_UI_sound(game_config::sounds::user_arrive);
00686         }
00687     }
00688 
00689     user_list_ = list;
00690 }
00691 
00692 void ui::append_to_title(const std::string& text) {
00693     title_.set_text(title_.get_text() + text);
00694 }
00695 
00696 const gui::label& ui::title() const
00697 {
00698     return title_;
00699 }
00700 
00701 
00702 }// namespace mp

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