multiplayer_lobby.cpp

Go to the documentation of this file.
00001 /* $Id: multiplayer_lobby.cpp 25781 2008-04-13 02:22:17Z dave $ */
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_lobby.cpp 
00016  * A section on the server where players can chat, create and join games.
00017  */
00018 
00019 #include "global.hpp"
00020 
00021 #include "config.hpp"
00022 #include "filesystem.hpp"
00023 #include "game_display.hpp"
00024 #include "marked-up_text.hpp"
00025 #include "minimap.hpp"
00026 #include "multiplayer_lobby.hpp"
00027 #include "replay.hpp"
00028 #include "wml_separators.hpp"
00029 #include "game_config.hpp"
00030 #include "gettext.hpp"
00031 #include "log.hpp"
00032 #include "playmp_controller.hpp"
00033 #include "show_dialog.hpp"
00034 #include "sound.hpp"
00035 #include "wml_exception.hpp"
00036 
00037 #define ERR_CF LOG_STREAM(err, config)
00038 
00039 namespace {
00040 std::vector<std::string> empty_string_vector;
00041 }
00042 
00043 namespace mp {
00044     gamebrowser::gamebrowser(CVideo& video, const config* map_hashes) :
00045     menu(video, empty_string_vector, false, -1, -1, NULL, &menu::bluebg_style),
00046     gold_icon_locator_("themes/gold.png"),
00047     xp_icon_locator_("themes/units.png"),
00048     vision_icon_locator_("misc/invisible.png"),
00049     time_limit_icon_locator_("themes/sand-clock.png"),
00050     observer_icon_locator_(game_config::observer_image),
00051     no_observer_icon_locator_("misc/no_observer.png"), map_hashes_(map_hashes),
00052     item_height_(100), margin_(5), minimap_size_(item_height_ - 2*margin_),  h_padding_(5),
00053     header_height_(20), selected_(0), visible_range_(std::pair<size_t,size_t>(0,0)),
00054     double_clicked_(false), ignore_next_doubleclick_(false), last_was_doubleclick_(false)
00055 {
00056     set_numeric_keypress_selection(false);
00057 }
00058 
00059 void gamebrowser::set_inner_location(const SDL_Rect& rect)
00060 {
00061     set_full_size(games_.size());
00062     set_shown_size(rect.h / row_height());
00063     bg_register(rect);
00064     scroll(get_position());
00065 }
00066 
00067 void gamebrowser::scroll(unsigned int pos)
00068 {
00069     if(pos < games_.size()) {
00070         visible_range_.first = pos;
00071         visible_range_.second = minimum<size_t>(pos + inner_location().h / row_height(), games_.size());
00072         set_dirty();
00073     }
00074 }
00075 
00076 SDL_Rect gamebrowser::get_item_rect(size_t index) const {
00077     if(index < visible_range_.first || index > visible_range_.second) {
00078         const SDL_Rect res = { 0, 0, 0, 0 };
00079         return res;
00080     }
00081     const SDL_Rect& loc = inner_location();
00082     const SDL_Rect res = { loc.x, loc.y + (index - visible_range_.first) * row_height(), loc.w, row_height()};
00083     return res;
00084 }
00085 
00086 void gamebrowser::draw()
00087 {
00088     if(hidden())
00089         return;
00090     if(dirty()) {
00091         bg_restore();
00092         util::scoped_ptr<clip_rect_setter> clipper(NULL);
00093         if(clip_rect())
00094             clipper.assign(new clip_rect_setter(video().getSurface(), *clip_rect()));
00095         draw_contents();
00096         update_rect(location());
00097         set_dirty(false);
00098     }
00099 }
00100 
00101 void gamebrowser::draw_contents()
00102 {
00103     if(!games_.empty()) {
00104         for(size_t i = visible_range_.first; i != visible_range_.second; ++i) {
00105             style_->draw_row(*this,i,get_item_rect(i),(i==selected_)? SELECTED_ROW : NORMAL_ROW);
00106         }
00107     } else {
00108         const SDL_Rect rect = inner_location();
00109         font::draw_text(&video(), rect, font::SIZE_NORMAL, font::NORMAL_COLOUR, _("--no games open--"), rect.x + margin_, rect.y + margin_);
00110     }
00111 }
00112 
00113 void gamebrowser::draw_row(const size_t index, const SDL_Rect& item_rect, ROW_TYPE /*type*/) {
00114     const game_item& game = games_[index];
00115     int xpos = item_rect.x + margin_;
00116     int ypos = item_rect.y + margin_;
00117 
00118     // Draw minimaps
00119     if (game.mini_map != NULL) {
00120         int minimap_x = xpos + (minimap_size_ - game.mini_map->w)/2;
00121         int minimap_y = ypos + (minimap_size_ - game.mini_map->h)/2;
00122         video().blit_surface(minimap_x, minimap_y, game.mini_map);
00123     }
00124     xpos += minimap_size_ + margin_;
00125 
00126     // Set font color
00127     SDL_Color font_color;
00128     if (!game.password_required) {
00129         font_color = font::BAD_COLOUR;
00130     } else if (game.vacant_slots > 0) {
00131         if (game.reloaded || game.started) {
00132             font_color = font::YELLOW_COLOUR;
00133         } else {
00134             font_color = font::GOOD_COLOUR;
00135         }
00136     } else {
00137         if (game.observers) {
00138             font_color = font::NORMAL_COLOUR;
00139         } else {
00140             font_color = font::BAD_COLOUR;
00141         }
00142     }
00143 
00144     const surface status_text(font::get_rendered_text(game.status,
00145         font::SIZE_NORMAL, font_color));
00146     const int status_text_width = status_text ? status_text->w : 0;
00147 
00148     // First line: draw game name
00149     const surface name_surf(font::get_rendered_text(
00150         font::make_text_ellipsis(game.name, font::SIZE_PLUS,
00151             (item_rect.x + item_rect.w) - xpos - margin_ - status_text_width - h_padding_),
00152         font::SIZE_PLUS, font_color));
00153     video().blit_surface(xpos, ypos, name_surf);
00154 
00155     // Draw status text
00156     if(status_text) {
00157         // Align the bottom of the text with the game name
00158         video().blit_surface(item_rect.x + item_rect.w - margin_ - status_text_width,
00159             ypos + name_surf->h - status_text->h, status_text);
00160     }
00161 
00162     // Second line
00163     ypos = item_rect.y + item_rect.h/2;
00164 
00165     // Draw map info
00166     const surface map_info_surf(font::get_rendered_text(
00167         font::make_text_ellipsis(game.map_info, font::SIZE_NORMAL,
00168             (item_rect.x + item_rect.w) - xpos - margin_),
00169         font::SIZE_NORMAL, font::NORMAL_COLOUR));
00170     if(map_info_surf) {
00171         video().blit_surface(xpos, ypos - map_info_surf->h/2, map_info_surf);
00172     }
00173 
00174     // Third line
00175     ypos = item_rect.y + item_rect.h  - margin_;
00176 
00177     // Draw observer icon
00178     const surface observer_icon(image::get_image(game.observers
00179         ? observer_icon_locator_ : no_observer_icon_locator_));
00180     if(observer_icon) {
00181         video().blit_surface(xpos, ypos - observer_icon->h, observer_icon);
00182 
00183         // Set ypos to the middle of the line, so that 
00184         // all text and icons can be aligned symmetrical to it
00185         ypos -= observer_icon->h/2;
00186         xpos += observer_icon->w + 2 * h_padding_;
00187     }
00188 
00189     // Draw gold icon
00190     const surface gold_icon(image::get_image(gold_icon_locator_));
00191     if(gold_icon) {
00192         video().blit_surface(xpos, ypos - gold_icon->h/2, gold_icon);
00193 
00194         xpos += gold_icon->w + h_padding_;
00195     }
00196 
00197     // Draw gold text
00198     const surface gold_text(font::get_rendered_text(game.gold, font::SIZE_NORMAL,
00199         game.use_map_settings ? font::GRAY_COLOUR : font::NORMAL_COLOUR));
00200     if(gold_text) {
00201         video().blit_surface(xpos, ypos - gold_text->h/2, gold_text);
00202 
00203         xpos += gold_text->w + 2 * h_padding_;
00204     }
00205 
00206     // Draw xp icon
00207     const surface xp_icon(image::get_image(xp_icon_locator_));
00208     if(xp_icon) {
00209         video().blit_surface(xpos, ypos - xp_icon->h/2, xp_icon);
00210 
00211         xpos += xp_icon->w + h_padding_;
00212     }
00213 
00214     // Draw xp text
00215     const surface xp_text(font::get_rendered_text(game.xp, font::SIZE_NORMAL,
00216         font::NORMAL_COLOUR));
00217     if(xp_text) {
00218         video().blit_surface(xpos, ypos - xp_text->h/2, xp_text);
00219 
00220         xpos += xp_text->w + 2 * h_padding_;
00221     }
00222 
00223     if(!game.time_limit.empty()) {
00224         // Draw time icon
00225         const surface time_icon(image::get_image(time_limit_icon_locator_));
00226         video().blit_surface(xpos, ypos - time_icon->h/2, time_icon);
00227 
00228         xpos += time_icon->w + h_padding_;
00229 
00230         // Draw time text
00231         const surface time_text(font::get_rendered_text(game.time_limit,
00232             font::SIZE_NORMAL, font::NORMAL_COLOUR));
00233         video().blit_surface(xpos, ypos - time_text->h/2, time_text);
00234 
00235         xpos += time_text->w + 2 * h_padding_;
00236     }
00237 
00238     // Draw vision icon
00239     const surface vision_icon(image::get_image(vision_icon_locator_));
00240     if(vision_icon) {
00241         video().blit_surface(xpos, ypos - vision_icon->h/2, vision_icon);
00242 
00243         xpos += vision_icon->w + h_padding_;
00244     }
00245 
00246     // Draw vision text
00247     const surface vision_text(font::get_rendered_text(
00248         font::make_text_ellipsis(game.vision, font::SIZE_NORMAL,
00249             (item_rect.x + item_rect.w) - xpos - margin_),
00250         font::SIZE_NORMAL,
00251         game.use_map_settings ? font::GRAY_COLOUR : font::NORMAL_COLOUR));
00252     if(vision_text) {
00253         video().blit_surface(xpos, ypos - vision_text->h/2, vision_text);
00254     }
00255 
00256     // Draw map settings text
00257     if (game.use_map_settings) {
00258         xpos += vision_text->w + 4 * h_padding_;
00259         const surface map_settings_text(font::get_rendered_text(
00260             font::make_text_ellipsis(_("Use map settings"), font::SIZE_NORMAL,
00261                 (item_rect.x + item_rect.w) - xpos - margin_),
00262             font::SIZE_NORMAL,
00263             (game.verified && game.vacant_slots > 0)
00264                 ? font::GOOD_COLOUR : font::NORMAL_COLOUR));
00265         video().blit_surface(xpos, ypos - map_settings_text->h/2, map_settings_text);
00266     }
00267 }
00268 
00269 void gamebrowser::handle_event(const SDL_Event& event)
00270 {
00271     scrollarea::handle_event(event);
00272     if(event.type == SDL_KEYDOWN) {
00273         if(focus(&event) && !games_.empty()) {
00274             switch(event.key.keysym.sym) {
00275                 case SDLK_UP:
00276                     if(selected_ > 0) {
00277                         --selected_;
00278                         adjust_position(selected_);
00279                         set_dirty();
00280                     }
00281                     break;
00282                 case SDLK_DOWN:
00283                     if(selected_ < games_.size() - 1) {
00284                         ++selected_;
00285                         adjust_position(selected_);
00286                         set_dirty();
00287                     }
00288                     break;
00289                 case SDLK_PAGEUP:
00290                 {
00291                     const long items_on_screen = visible_range_.second - visible_range_.first;
00292                     selected_ = static_cast<size_t>(maximum<long>(static_cast<long>(selected_) - items_on_screen, 0));
00293                     adjust_position(selected_);
00294                     set_dirty();
00295                 }
00296                     break;
00297                 case SDLK_PAGEDOWN:
00298                 {
00299                     const size_t items_on_screen = visible_range_.second - visible_range_.first;
00300                     selected_ = minimum<size_t>(selected_ + items_on_screen, games_.size() - 1);
00301                     adjust_position(selected_);
00302                     set_dirty();
00303                 }
00304                     break;
00305                 case SDLK_HOME:
00306                     selected_ = 0;
00307                     adjust_position(selected_);
00308                     set_dirty();
00309                     break;
00310                 case SDLK_END:
00311                     selected_ = games_.size() - 1;
00312                     adjust_position(selected_);
00313                     set_dirty();
00314                     break;
00315                 default:
00316                     break;
00317             }
00318         }
00319     } else if((event.type == SDL_MOUSEBUTTONDOWN && event.button.button == SDL_BUTTON_LEFT) || event.type == DOUBLE_CLICK_EVENT) {
00320         int x = 0;
00321         int y = 0;
00322         if(event.type == SDL_MOUSEBUTTONDOWN) {
00323             x = event.button.x;
00324             y = event.button.y;
00325         } else {
00326             x = (long)event.user.data1;
00327             y = (long)event.user.data2;
00328         }
00329         const SDL_Rect& loc = inner_location();
00330 
00331         if(!games_.empty() && point_in_rect(x, y, loc)) {
00332             for(size_t i = visible_range_.first; i != visible_range_.second; ++i) {
00333                 const SDL_Rect& item_rect = get_item_rect(i);
00334 
00335                 if(point_in_rect(x, y, item_rect)) {
00336                     set_focus(true);
00337                     selected_ = i;
00338                     break;
00339                 }
00340             }
00341             if(event.type == DOUBLE_CLICK_EVENT) {
00342                 if (ignore_next_doubleclick_) {
00343                     ignore_next_doubleclick_ = false;
00344                 } else if(selection_is_joinable() || selection_is_observable()) {
00345                     double_clicked_ = true;
00346                     last_was_doubleclick_ = true;
00347                 }
00348             } else if (last_was_doubleclick_) {
00349                 // If we have a double click as the next event, it means
00350                 // this double click was generated from a click that
00351                 // already has helped in generating a double click.
00352                 // ??
00353                 SDL_Event ev;
00354                 SDL_PeepEvents(&ev, 1, SDL_PEEKEVENT,
00355                                SDL_EVENTMASK(DOUBLE_CLICK_EVENT));
00356                 if (ev.type == DOUBLE_CLICK_EVENT) {
00357                     ignore_next_doubleclick_ = true;
00358                 }
00359                 last_was_doubleclick_ = false;
00360             }
00361         }
00362     }
00363 }
00364 
00365 struct minimap_cache_item {
00366     std::string map_data;
00367     surface mini_map;
00368     std::string map_info_size;
00369 };
00370 
00371 void gamebrowser::set_game_items(const config& cfg, const config& game_config)
00372 {
00373     const bool scrolled_to_max = (has_scrollbar() && get_position() == get_max_position());
00374     const bool selection_visible = (selected_ >= visible_range_.first && selected_ <= visible_range_.second);
00375     const std::string selected_game = (selected_ < games_.size()) ? games_[selected_].id : "";
00376 
00377     item_height_ = 100;
00378 
00379     // Don't throw the rendered minimaps away
00380     std::vector<minimap_cache_item> minimap_cache;
00381     for(std::vector<game_item>::iterator oldgame = games_.begin(); oldgame != games_.end(); ++oldgame) {
00382         minimap_cache_item item;
00383         item.map_data = oldgame->map_data;
00384         item.mini_map = oldgame->mini_map;
00385         item.map_info_size = oldgame->map_info_size;
00386         minimap_cache.push_back(item);
00387     }
00388 
00389     games_.clear();
00390     config::child_list games = cfg.get_children("game");
00391     config::child_iterator game;
00392 
00393     for(game = games.begin(); game != games.end(); ++game) {
00394         bool verified = true;
00395         games_.push_back(game_item());
00396         games_.back().password_required = (**game)["password"] == "yes";
00397         games_.back().reloaded = (**game)["savegame"] == "yes";
00398         games_.back().have_era = true;
00399         if((**game)["mp_era"] != "") {
00400             const config* const era_cfg = game_config.find_child("era", "id", (**game)["mp_era"]);
00401             utils::string_map symbols;
00402             symbols["era_id"] = (**game)["mp_era"];
00403             if (era_cfg != NULL) {
00404                 games_.back().map_info = era_cfg->get_attribute("name");
00405             } else {
00406                 if((**game)["require_era"] == "yes") {
00407                     games_.back().have_era = false;
00408                 }
00409                 games_.back().map_info = vgettext("Unknown era: $era_id", symbols);
00410                 verified = false;
00411             }
00412         } else {
00413             games_.back().map_info = _("Unknown era");
00414             verified = false;
00415         }
00416         games_.back().map_data = (**game)["map_data"];
00417         if(games_.back().map_data.empty()) {
00418             games_.back().map_data = read_map((**game)["map"]);
00419         }
00420 
00421         if(! games_.back().map_data.empty()) {
00422             try {
00423                 std::vector<minimap_cache_item>::iterator i;
00424                 bool found = false;
00425                 for(i = minimap_cache.begin(); i != minimap_cache.end() && !found; ++i) {
00426                     if (i->map_data == games_.back().map_data) {
00427                         found = true;
00428                         games_.back().map_info_size = i->map_info_size;
00429                         games_.back().mini_map = i->mini_map;
00430                     }
00431                 }
00432                 if (!found) {
00433                     // Parsing the map and generating the minimap are both cpu expensive
00434                     gamemap map(game_config, games_.back().map_data); 
00435                     games_.back().mini_map = image::getMinimap(minimap_size_, minimap_size_, map, 0);
00436                     games_.back().map_info_size = lexical_cast_default<std::string, int>(map.w(), "??")
00437                         + std::string("x") + lexical_cast_default<std::string, int>(map.h(), "??");
00438                 }
00439                 games_.back().map_info += " - " + games_.back().map_info_size;
00440             } catch(gamemap::incorrect_format_exception &e) {
00441                 ERR_CF << "illegal map: " << e.msg_ << "\n";
00442                 verified = false;
00443             } catch(twml_exception& e) {
00444                 ERR_CF <<  "map could not be loaded: " << e.dev_message << '\n';
00445                 verified = false;
00446             }
00447         } else {
00448             games_.back().map_info += " - ??x??";
00449         }
00450         games_.back().map_info += " ";
00451         if((**game)["mp_scenario"] != "") {
00452             // check if it's a multiplayer scenario
00453             const config* level_cfg = game_config.find_child("multiplayer", "id", (**game)["mp_scenario"]);
00454             if(level_cfg == NULL) {
00455                 // check if it's a user map
00456                 level_cfg = game_config.find_child("generic_multiplayer", "id", (**game)["mp_scenario"]);
00457             }
00458             if(level_cfg) {
00459                 games_.back().map_info += level_cfg->get_attribute("name");
00460                 // reloaded games do not match the original scenario hash,
00461                 // so it makes no sense to test them, they always would appear
00462                 // as remote scenarios
00463                 if (map_hashes_ && !games_.back().reloaded) {
00464                     const std::string& hash = (**game)["hash"];
00465                     bool hash_found = false;
00466                     for(string_map::const_iterator i = map_hashes_->values.begin(); i != map_hashes_->values.end(); ++i) {
00467                         if(i->first == (**game)["mp_scenario"] && i->second == hash) {
00468                             hash_found = true;
00469                             break;
00470                         }
00471                     }
00472                     if(!hash_found) {
00473                         games_.back().map_info += " - ";
00474                         games_.back().map_info += _("Remote scenario");
00475                         verified = false;
00476                     }
00477                 }
00478             } else {
00479                 utils::string_map symbols;
00480                 symbols["scenario_id"] = (**game)["mp_scenario"];
00481                 games_.back().map_info += vgettext("Unknown scenario: $scenario_id", symbols);
00482                 verified = false;
00483             }
00484         } else {
00485             games_.back().map_info += _("Unknown scenario");
00486             verified = false;
00487         }
00488         if (games_.back().reloaded) {
00489             games_.back().map_info += " - ";
00490             games_.back().map_info += _("Reloaded game");
00491             verified = false;
00492         }
00493         games_.back().id = (**game)["id"];
00494         games_.back().name = (**game)["name"];
00495         const std::string& turn = (**game)["turn"];
00496         const std::string& slots = (**game)["slots"];
00497         games_.back().vacant_slots = lexical_cast_default<size_t>(slots, 0);
00498         games_.back().current_turn = 0;
00499         if(turn != "") {
00500             games_.back().started = true;
00501             int index = turn.find_first_of('/');
00502             if (index > -1){
00503                 const std::string current_turn = turn.substr(0, index);
00504                 games_.back().current_turn = lexical_cast<unsigned int>(current_turn);
00505             }
00506             games_.back().status = _("Turn ") + turn;
00507         } else {
00508             games_.back().started = false;
00509             if (games_.back().vacant_slots > 0) {
00510                 games_.back().status = std::string(_n("Vacant Slot:", "Vacant Slots:",
00511                         games_.back().vacant_slots)) + " " + slots;
00512                 if (games_.back().password_required) {
00513                     games_.back().status += std::string(" (") + std::string(_("Password Required")) + ")";
00514                 }
00515             }
00516         }
00517 
00518         games_.back().use_map_settings = ((**game)["mp_use_map_settings"] == "yes");
00519         games_.back().gold = (**game)["mp_village_gold"];
00520         if((**game)["mp_fog"] == "yes") {
00521             games_.back().vision = _("Fog");
00522             games_.back().fog = true;
00523             if((**game)["mp_shroud"] == "yes") {
00524                 games_.back().vision += "/";
00525                 games_.back().vision += _("Shroud");
00526                 games_.back().shroud = true;
00527             } else {
00528                 games_.back().shroud = false;
00529             }
00530         } else if((**game)["mp_shroud"] == "yes") {
00531             games_.back().vision = _("Shroud");
00532             games_.back().fog = false;
00533             games_.back().shroud = true;
00534         } else {
00535             games_.back().vision = _("none");
00536             games_.back().fog = false;
00537             games_.back().shroud = false;
00538         }
00539         if((**game)["mp_countdown"] == "yes" ) {
00540             games_.back().time_limit =   (**game)["mp_countdown_init_time"] + " / +"
00541                                        + (**game)["mp_countdown_turn_bonus"] + " "
00542                                        + (**game)["mp_countdown_action_bonus"];
00543         } else {
00544             games_.back().time_limit = "";
00545         }
00546         games_.back().xp = (**game)["experience_modifier"] + "%";
00547         games_.back().observers = (**game)["observer"] != "no" ? true : false;
00548         games_.back().verified = verified;
00549     }
00550     set_full_size(games_.size());
00551     set_shown_size(inner_location().h / row_height());
00552 
00553     // Try to preserve the game selection
00554     if (!selected_game.empty()) {
00555         for (unsigned int i=0; i < games_.size(); i++) {
00556             if (games_[i].id == selected_game) {
00557                 selected_ = i;
00558                 break;
00559             }
00560         }
00561     }
00562     if(selected_ >= games_.size())
00563         selected_ = maximum<long>(static_cast<long>(games_.size()) - 1, 0);
00564 
00565     if (scrolled_to_max) {
00566         set_position(get_max_position());
00567     } else {
00568         // Keep the selected game visible if it was visible before
00569         if (selection_visible && (visible_range_.first > selected_
00570                                   || visible_range_.second < selected_)) {
00571             set_position(selected_);
00572         }
00573     }
00574     scroll(get_position());
00575     set_dirty();
00576 }
00577 
00578 lobby::lobby_sorter::lobby_sorter(const config& cfg) : cfg_(cfg)
00579 {
00580     set_alpha_sort(1);
00581 }
00582 
00583 bool lobby::lobby_sorter::column_sortable(int column) const
00584 {
00585     switch(column)
00586     {
00587     case MAP_COLUMN:
00588     case STATUS_COLUMN:
00589         return true;
00590     default:
00591         return basic_sorter::column_sortable(column);
00592     }
00593 }
00594 
00595 bool lobby::lobby_sorter::less(int column, const gui::menu::item& row1, const gui::menu::item& row2) const
00596 {
00597     const config* const list = cfg_.child("gamelist");
00598     if(list == NULL) {
00599         return false;
00600     }
00601 
00602     const config::child_list& games = list->get_children("game");
00603     if(row1.id >= games.size() || row2.id >= games.size()) {
00604         return false;
00605     }
00606 
00607     const config& game1 = *games[row1.id];
00608     const config& game2 = *games[row2.id];
00609 
00610     if(column == MAP_COLUMN) {
00611         size_t mapsize1 = game1["map_data"].size();
00612         if(mapsize1 == 0) {
00613             mapsize1 = game1["map"].size();
00614         }
00615 
00616         size_t mapsize2 = game2["map_data"].size();
00617         if(mapsize2 == 0) {
00618             mapsize2 = game2["map"].size();
00619         }
00620 
00621         return mapsize1 < mapsize2;
00622 
00623     } else if(column == STATUS_COLUMN) {
00624         const int nslots1 = atoi(game1["slots"].c_str());
00625         const int nslots2 = atoi(game2["slots"].c_str());
00626 
00627         const int turn1 = atoi(game1["turn"].c_str());
00628         const int turn2 = atoi(game2["turn"].c_str());
00629 
00630         if(nslots1 > nslots2) {
00631             return true;
00632         } else if(nslots1 < nslots2) {
00633             return false;
00634         } else {
00635             return turn1 < turn2;
00636         }
00637     } else {
00638         return basic_sorter::less(column,row1,row2);
00639     }
00640 
00641     return false;
00642 }
00643 
00644 lobby::lobby(game_display& disp, const config& cfg, chat& c, config& gamelist) :
00645     mp::ui(disp, _("Game Lobby"), cfg, c, gamelist),
00646 
00647     observe_game_(disp.video(), _("Observe Game")),
00648     join_game_(disp.video(), _("Join Game")),
00649     create_game_(disp.video(), _("Create Game")),
00650     skip_replay_(disp.video(), _("Quick Replays"), gui::button::TYPE_CHECK),
00651 #ifndef USE_TINY_GUI
00652     game_preferences_(disp.video(), _("Preferences")),
00653 #endif
00654     quit_game_(disp.video(), _("Quit")),
00655     last_selected_game_(-1), sorter_(gamelist),
00656     games_menu_(disp.video(),cfg.child("multiplayer_hashes"))
00657 {
00658     skip_replay_.set_check(preferences::skip_mp_replay());
00659     skip_replay_.set_help_string(_("Skip quickly to the active turn when observing"));
00660     game_config::debug = false;
00661     gamelist_updated();
00662     sound::play_music_repeatedly(game_config::lobby_music);
00663 }
00664 
00665 void lobby::hide_children(bool hide)
00666 {
00667     ui::hide_children(hide);
00668 
00669     games_menu_.hide(hide);
00670     observe_game_.hide(hide);
00671     join_game_.hide(hide);
00672     create_game_.hide(hide);
00673     skip_replay_.hide(hide);
00674 #ifndef USE_TINY_GUI
00675     game_preferences_.hide(hide);
00676 #endif
00677     quit_game_.hide(hide);
00678 }
00679 
00680 void lobby::layout_children(const SDL_Rect& rect)
00681 {
00682     ui::layout_children(rect);
00683 
00684 #ifdef USE_TINY_GUI
00685     int btn_space = 3;
00686     int xborder   = 0;
00687     int yborder   = 0;
00688 #else
00689     int btn_space = 5;
00690     int xborder   = 10;
00691     int yborder   = 7;
00692 #endif
00693 
00694     // Align to the left border
00695     join_game_.set_location(xscale(xborder), yscale(yborder));
00696     observe_game_.set_location(join_game_.location().x + join_game_.location().w + btn_space, yscale(yborder));
00697     create_game_.set_location(observe_game_.location().x + observe_game_.location().w + btn_space, yscale(yborder));
00698 
00699 #ifndef USE_TINY_GUI
00700     // Align 'Quit' to the right border
00701     quit_game_.set_location(xscale(xscale_base - xborder) - quit_game_.location().w, yscale(yborder));
00702 
00703     // Align in the middle between the right and left buttons
00704     int space = (quit_game_.location().x - create_game_.location().x - create_game_.location().w
00705                  - skip_replay_.location().w - game_preferences_.location().w - btn_space) / 2;
00706     if (space < btn_space) space = btn_space;
00707     skip_replay_.set_location(create_game_.location().x + create_game_.location().w + space, yscale(yborder));
00708     game_preferences_.set_location(quit_game_.location().x - game_preferences_.location().w - space, yscale(yborder));
00709 #else
00710     skip_replay_.set_location(create_game_.location().x + create_game_.location().w, yscale(yborder));
00711     quit_game_.set_location(skip_replay_.location().x + skip_replay_.location().w + btn_space, yscale(yborder));
00712 #endif
00713 
00714     games_menu_.set_location(client_area().x, client_area().y + title().height());
00715     games_menu_.set_measurements(client_area().w, client_area().h
00716             - title().height() - gui::ButtonVPadding);
00717 }
00718 
00719 void lobby::gamelist_updated(bool silent)
00720 {
00721     ui::gamelist_updated(silent);
00722 
00723     const config* list = gamelist().child("gamelist");
00724     if(list == NULL) {
00725         // No gamelist yet. Do not update anything.
00726         return;
00727     }
00728     games_menu_.set_game_items(*list, game_config());
00729     join_game_.enable(games_menu_.selection_is_joinable());
00730     observe_game_.enable(games_menu_.selection_is_observable());
00731 }
00732 
00733 void lobby::process_event()
00734 {
00735     join_game_.enable(games_menu_.selection_is_joinable());
00736     observe_game_.enable(games_menu_.selection_is_observable());
00737 
00738     const bool observe = (observe_game_.pressed() || (games_menu_.selected() && !games_menu_.selection_is_joinable())) && games_menu_.selection_is_observable();
00739     const bool join = (join_game_.pressed() || games_menu_.selected()) && games_menu_.selection_is_joinable();
00740     games_menu_.reset_selection();
00741     preferences::set_skip_mp_replay(skip_replay_.checked());
00742     playmp_controller::set_replay_last_turn(0);
00743     preferences::set_message_private(false);
00744 
00745     int selected_game = games_menu_.selection();
00746     if (selected_game != last_selected_game_) {
00747         if (games_menu_.empty()) {
00748             set_selected_game("");
00749         } else {
00750             set_selected_game(games_menu_.selected_game().id);
00751         }
00752         ui::gamelist_updated();
00753         last_selected_game_ = selected_game;
00754     }
00755 
00756     if(join || observe) {
00757         const int selected = games_menu_.selection();
00758         if(!games_menu_.empty() && selected >= 0) {
00759             gamebrowser::game_item game = games_menu_.selected_game();
00760 
00761             std::string password;
00762             if(join && game.password_required) {
00763                 const int res = gui::show_dialog(disp_, NULL, _("Password Required"),
00764                           _("Joining this game requires a password."),
00765                           gui::OK_CANCEL, NULL, NULL, _("Password: "), &password);
00766                 if(res != 0) {
00767                     return;
00768                 }
00769             }
00770 
00771             config response;
00772             config& join = response.add_child("join");
00773             join["id"] = game.id;
00774             if (observe){
00775                 join["observe"] = "yes";
00776             }
00777             else{
00778                 join["observe"] = "no";
00779             }
00780 
00781             if(!password.empty()) {
00782                 join["password"] = password;
00783             }
00784             network::send_data(response, 0, true);
00785 
00786             if(observe) {
00787                 if (game.started){
00788                     playmp_controller::set_replay_last_turn(game.current_turn);
00789                 }
00790                 set_result(OBSERVE);
00791             } else {
00792                 set_result(JOIN);
00793             }
00794         }
00795         return;
00796     }
00797 
00798     if(create_game_.pressed()) {
00799         set_result(CREATE);
00800         return;
00801     }
00802 
00803 #ifndef USE_TINY_GUI
00804     if(game_preferences_.pressed()) {
00805         set_result(PREFERENCES);
00806         return;
00807     }
00808 #endif
00809 
00810     if(quit_game_.pressed()) {
00811         recorder.set_skip(false);
00812         set_result(QUIT);
00813         return;
00814     }
00815 }
00816 
00817 void lobby::process_network_data(const config& data, const network::connection sock)
00818 {
00819     ui::process_network_data(data, sock);
00820 
00821     // Invalidate game selection for the player list
00822     last_selected_game_ = -1;
00823 }
00824 
00825 } // end namespace mp
00826 

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