00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include "global.hpp"
00021
00022 #include "filesystem.hpp"
00023 #include "foreach.hpp"
00024 #include "game_preferences.hpp"
00025 #include "lobby_preferences.hpp"
00026 #include "map_exception.hpp"
00027 #include "marked-up_text.hpp"
00028 #include "minimap.hpp"
00029 #include "multiplayer_lobby.hpp"
00030 #include "gettext.hpp"
00031 #include "log.hpp"
00032 #include "playmp_controller.hpp"
00033 #include "sound.hpp"
00034 #include "wml_exception.hpp"
00035 #include "formula_string_utils.hpp"
00036
00037 static lg::log_domain log_config("config");
00038 #define ERR_CF LOG_STREAM(err, log_config)
00039
00040 namespace {
00041 std::vector<std::string> empty_string_vector;
00042 }
00043
00044 namespace mp {
00045 gamebrowser::gamebrowser(CVideo& video, const config &map_hashes) :
00046 menu(video, empty_string_vector, false, -1, -1, NULL, &menu::bluebg_style),
00047 gold_icon_locator_("themes/gold.png"),
00048 xp_icon_locator_("themes/units.png"),
00049 vision_icon_locator_("misc/invisible.png"),
00050 time_limit_icon_locator_("themes/sand-clock.png"),
00051 observer_icon_locator_("misc/eye.png"),
00052 no_observer_icon_locator_("misc/no_observer.png"),
00053 shuffle_sides_icon_locator_("misc/shuffle-sides.png"),
00054 map_hashes_(map_hashes),
00055 item_height_(100),
00056 margin_(5),
00057 minimap_size_(item_height_ - 2*margin_),
00058 h_padding_(5),
00059 header_height_(20),
00060 selected_(0),
00061 visible_range_(std::pair<size_t,size_t>(0,0)),
00062 games_(),
00063 redraw_items_(),
00064 widths_(),
00065 double_clicked_(false),
00066 ignore_next_doubleclick_(false),
00067 last_was_doubleclick_(false)
00068 {
00069 set_numeric_keypress_selection(false);
00070 }
00071
00072 void gamebrowser::set_inner_location(const SDL_Rect& rect)
00073 {
00074 set_full_size(games_.size());
00075 set_shown_size(rect.h / row_height());
00076 bg_register(rect);
00077 scroll(get_position());
00078 }
00079
00080 void gamebrowser::scroll(unsigned int pos)
00081 {
00082 if(pos < games_.size()) {
00083 visible_range_.first = pos;
00084 visible_range_.second = std::min<size_t>(pos + inner_location().h / row_height(), games_.size());
00085 set_dirty();
00086 }
00087 }
00088
00089 SDL_Rect gamebrowser::get_item_rect(size_t index) const {
00090 if(index < visible_range_.first || index > visible_range_.second) {
00091 const SDL_Rect res = { 0, 0, 0, 0 };
00092 return res;
00093 }
00094 const SDL_Rect& loc = inner_location();
00095 return create_rect(
00096 loc.x
00097 , loc.y + (index - visible_range_.first) * row_height()
00098 , loc.w
00099 , row_height());
00100 }
00101
00102 void gamebrowser::draw()
00103 {
00104 if(hidden())
00105 return;
00106 if(dirty()) {
00107 bg_restore();
00108 util::scoped_ptr<clip_rect_setter> clipper(NULL);
00109 if(clip_rect())
00110 clipper.assign(new clip_rect_setter(video().getSurface(), clip_rect()));
00111 draw_contents();
00112 update_rect(location());
00113 set_dirty(false);
00114 }
00115 }
00116
00117 void gamebrowser::draw_contents()
00118 {
00119 if(!games_.empty()) {
00120 for(size_t i = visible_range_.first; i != visible_range_.second; ++i) {
00121 style_->draw_row(*this,i,get_item_rect(i),(i==selected_)? SELECTED_ROW : NORMAL_ROW);
00122 }
00123 } else {
00124 const SDL_Rect rect = inner_location();
00125 font::draw_text(&video(), rect, font::SIZE_NORMAL, font::NORMAL_COLOR, _("--no games open--"), rect.x + margin_, rect.y + margin_);
00126 }
00127 }
00128
00129 void gamebrowser::draw_row(const size_t index, const SDL_Rect& item_rect, ROW_TYPE ) {
00130 const game_item& game = games_[index];
00131 int xpos = item_rect.x + margin_;
00132 int ypos = item_rect.y + margin_;
00133 std::string no_era_string = "";
00134
00135 if (game.mini_map != NULL) {
00136 int minimap_x = xpos + (minimap_size_ - game.mini_map->w)/2;
00137 int minimap_y = ypos + (minimap_size_ - game.mini_map->h)/2;
00138 video().blit_surface(minimap_x, minimap_y, game.mini_map);
00139 }
00140 xpos += minimap_size_ + margin_;
00141
00142
00143 SDL_Color font_color;
00144 if (game.vacant_slots > 0) {
00145 if (game.reloaded || game.started) {
00146 font_color = font::YELLOW_COLOR;
00147 } else {
00148 font_color = font::GOOD_COLOR;
00149 }
00150 } else {
00151 if (game.observers) {
00152 font_color = font::NORMAL_COLOR;
00153 } else {
00154 font_color = font::BAD_COLOR;
00155 }
00156 }
00157 if(!game.have_era && font_color != font::BAD_COLOR) {
00158 font_color = font::DISABLED_COLOR;
00159 no_era_string = _(" (Unknown Era)");
00160 }
00161
00162 const surface status_text(font::get_rendered_text(game.status,
00163 font::SIZE_NORMAL, font_color));
00164 const int status_text_width = status_text ? status_text->w : 0;
00165
00166
00167 const surface name_surf(font::get_rendered_text(
00168 font::make_text_ellipsis(game.name + no_era_string, font::SIZE_PLUS,
00169 (item_rect.x + item_rect.w) - xpos - margin_ - status_text_width - h_padding_),
00170 font::SIZE_PLUS, font_color));
00171 video().blit_surface(xpos, ypos, name_surf);
00172
00173
00174 if(status_text) {
00175
00176 video().blit_surface(item_rect.x + item_rect.w - margin_ - status_text_width,
00177 ypos + name_surf->h - status_text->h, status_text);
00178 }
00179
00180
00181 ypos = item_rect.y + item_rect.h/2;
00182
00183
00184 const surface map_info_surf(font::get_rendered_text(
00185 font::make_text_ellipsis(game.map_info, font::SIZE_NORMAL,
00186 (item_rect.x + item_rect.w) - xpos - margin_),
00187 font::SIZE_NORMAL, font::NORMAL_COLOR));
00188 if(map_info_surf) {
00189 video().blit_surface(xpos, ypos - map_info_surf->h/2, map_info_surf);
00190 }
00191
00192
00193 ypos = item_rect.y + item_rect.h - margin_;
00194
00195
00196 const surface observer_icon(image::get_image(game.observers
00197 ? observer_icon_locator_ : no_observer_icon_locator_));
00198 if(observer_icon) {
00199 video().blit_surface(xpos, ypos - observer_icon->h, observer_icon);
00200
00201
00202
00203 ypos -= observer_icon->h/2;
00204 xpos += observer_icon->w + 2 * h_padding_;
00205 }
00206
00207
00208 if (game.shuffle_sides)
00209 {
00210 const surface shuffle_icon(image::get_image(shuffle_sides_icon_locator_));
00211 if(shuffle_icon) {
00212 video().blit_surface(xpos, ypos - shuffle_icon->h/2, shuffle_icon);
00213
00214 xpos += shuffle_icon->w + 2 * h_padding_;
00215 }
00216 }
00217
00218
00219 const surface gold_icon(image::get_image(gold_icon_locator_));
00220 if(gold_icon) {
00221 video().blit_surface(xpos, ypos - gold_icon->h/2, gold_icon);
00222
00223 xpos += gold_icon->w + h_padding_;
00224 }
00225
00226
00227 const surface gold_text(font::get_rendered_text(game.gold, font::SIZE_NORMAL,
00228 game.use_map_settings ? font::GRAY_COLOR : font::NORMAL_COLOR));
00229 if(gold_text) {
00230 video().blit_surface(xpos, ypos - gold_text->h/2, gold_text);
00231
00232 xpos += gold_text->w + 2 * h_padding_;
00233 }
00234
00235
00236 const surface xp_icon(image::get_image(xp_icon_locator_));
00237 if(xp_icon) {
00238 video().blit_surface(xpos, ypos - xp_icon->h/2, xp_icon);
00239
00240 xpos += xp_icon->w + h_padding_;
00241 }
00242
00243
00244 const surface xp_text(font::get_rendered_text(game.xp, font::SIZE_NORMAL, font::NORMAL_COLOR));
00245 if(xp_text) {
00246 video().blit_surface(xpos, ypos - xp_text->h/2, xp_text);
00247
00248 xpos += xp_text->w + 2 * h_padding_;
00249 }
00250
00251 if(!game.time_limit.empty()) {
00252
00253 const surface time_icon(image::get_image(time_limit_icon_locator_));
00254 video().blit_surface(xpos, ypos - time_icon->h/2, time_icon);
00255
00256 xpos += time_icon->w + h_padding_;
00257
00258
00259 const surface time_text(font::get_rendered_text(game.time_limit,
00260 font::SIZE_NORMAL, font::NORMAL_COLOR));
00261 video().blit_surface(xpos, ypos - time_text->h/2, time_text);
00262
00263 xpos += time_text->w + 2 * h_padding_;
00264 }
00265
00266
00267 const surface vision_icon(image::get_image(vision_icon_locator_));
00268 if(vision_icon) {
00269 video().blit_surface(xpos, ypos - vision_icon->h/2, vision_icon);
00270
00271 xpos += vision_icon->w + h_padding_;
00272 }
00273
00274
00275 const surface vision_text(font::get_rendered_text(
00276 font::make_text_ellipsis(game.vision, font::SIZE_NORMAL,
00277 (item_rect.x + item_rect.w) - xpos - margin_),
00278 font::SIZE_NORMAL,
00279 game.use_map_settings ? font::GRAY_COLOR : font::NORMAL_COLOR));
00280 if(vision_text) {
00281 video().blit_surface(xpos, ypos - vision_text->h/2, vision_text);
00282 }
00283
00284
00285 if (game.use_map_settings) {
00286 xpos += vision_text->w + 4 * h_padding_;
00287 const surface map_settings_text(font::get_rendered_text(
00288 font::make_text_ellipsis(_("Use map settings"), font::SIZE_NORMAL,
00289 (item_rect.x + item_rect.w) - xpos - margin_),
00290 font::SIZE_NORMAL,
00291 (game.verified && game.vacant_slots > 0)
00292 ? font::GOOD_COLOR : font::NORMAL_COLOR));
00293 video().blit_surface(xpos, ypos - map_settings_text->h/2, map_settings_text);
00294 }
00295 }
00296
00297 void gamebrowser::handle_event(const SDL_Event& event)
00298 {
00299 scrollarea::handle_event(event);
00300 if(event.type == SDL_KEYDOWN) {
00301 if(focus(&event) && !games_.empty()) {
00302 switch(event.key.keysym.sym) {
00303 case SDLK_UP:
00304 if(selected_ > 0) {
00305 --selected_;
00306 adjust_position(selected_);
00307 set_dirty();
00308 }
00309 break;
00310 case SDLK_DOWN:
00311 if(selected_ < games_.size() - 1) {
00312 ++selected_;
00313 adjust_position(selected_);
00314 set_dirty();
00315 }
00316 break;
00317 case SDLK_PAGEUP:
00318 {
00319 const long items_on_screen = visible_range_.second - visible_range_.first;
00320 selected_ = static_cast<size_t>(std::max<long>(static_cast<long>(selected_) - items_on_screen, 0));
00321 adjust_position(selected_);
00322 set_dirty();
00323 }
00324 break;
00325 case SDLK_PAGEDOWN:
00326 {
00327 const size_t items_on_screen = visible_range_.second - visible_range_.first;
00328 selected_ = std::min<size_t>(selected_ + items_on_screen, games_.size() - 1);
00329 adjust_position(selected_);
00330 set_dirty();
00331 }
00332 break;
00333 case SDLK_HOME:
00334 selected_ = 0;
00335 adjust_position(selected_);
00336 set_dirty();
00337 break;
00338 case SDLK_END:
00339 selected_ = games_.size() - 1;
00340 adjust_position(selected_);
00341 set_dirty();
00342 break;
00343 default:
00344 break;
00345 }
00346 }
00347 } else if((event.type == SDL_MOUSEBUTTONDOWN && event.button.button == SDL_BUTTON_LEFT) || event.type == DOUBLE_CLICK_EVENT) {
00348 int x = 0;
00349 int y = 0;
00350 if(event.type == SDL_MOUSEBUTTONDOWN) {
00351 x = event.button.x;
00352 y = event.button.y;
00353 } else {
00354 x = reinterpret_cast<long>(event.user.data1);
00355 y = reinterpret_cast<long>(event.user.data2);
00356 }
00357 const SDL_Rect& loc = inner_location();
00358
00359 if(!games_.empty() && point_in_rect(x, y, loc)) {
00360 for(size_t i = visible_range_.first; i != visible_range_.second; ++i) {
00361 const SDL_Rect& item_rect = get_item_rect(i);
00362
00363 if(point_in_rect(x, y, item_rect)) {
00364 set_focus(true);
00365 selected_ = i;
00366 break;
00367 }
00368 }
00369 if(event.type == DOUBLE_CLICK_EVENT) {
00370 if (ignore_next_doubleclick_) {
00371 ignore_next_doubleclick_ = false;
00372 } else if(selection_is_joinable() || selection_is_observable()) {
00373 double_clicked_ = true;
00374 last_was_doubleclick_ = true;
00375 }
00376 } else if (last_was_doubleclick_) {
00377
00378
00379
00380
00381 SDL_Event ev;
00382 SDL_PeepEvents(&ev, 1, SDL_PEEKEVENT,
00383 SDL_EVENTMASK(DOUBLE_CLICK_EVENT));
00384 if (ev.type == DOUBLE_CLICK_EVENT) {
00385 ignore_next_doubleclick_ = true;
00386 }
00387 last_was_doubleclick_ = false;
00388 }
00389 }
00390 }
00391 }
00392
00393 struct minimap_cache_item {
00394
00395 minimap_cache_item() :
00396 map_data(),
00397 mini_map(),
00398 map_info_size()
00399 {
00400 }
00401
00402 std::string map_data;
00403 surface mini_map;
00404 std::string map_info_size;
00405 };
00406
00407 void gamebrowser::set_game_items(const config& cfg, const config& game_config)
00408 {
00409 const bool scrolled_to_max = (has_scrollbar() && get_position() == get_max_position());
00410 const bool selection_visible = (selected_ >= visible_range_.first && selected_ <= visible_range_.second);
00411 const std::string selected_game = (selected_ < games_.size()) ? games_[selected_].id : "";
00412
00413 item_height_ = 100;
00414
00415
00416 std::vector<minimap_cache_item> minimap_cache;
00417 for(std::vector<game_item>::iterator oldgame = games_.begin(); oldgame != games_.end(); ++oldgame) {
00418 minimap_cache_item item;
00419 item.map_data = oldgame->map_data;
00420 item.mini_map = oldgame->mini_map;
00421 item.map_info_size = oldgame->map_info_size;
00422 minimap_cache.push_back(item);
00423 }
00424
00425 games_.clear();
00426
00427 foreach (const config &game, cfg.child("gamelist").child_range("game"))
00428 {
00429 bool verified = true;
00430 games_.push_back(game_item());
00431 games_.back().password_required = game["password"].to_bool();
00432 games_.back().reloaded = game["savegame"].to_bool();
00433 games_.back().have_era = true;
00434 if (!game["mp_era"].empty())
00435 {
00436 const config &era_cfg = game_config.find_child("era", "id", game["mp_era"]);
00437 utils::string_map symbols;
00438 symbols["era_id"] = game["mp_era"];
00439 if (era_cfg) {
00440 games_.back().map_info = era_cfg["name"].str();
00441 } else {
00442 if (!game["require_era"].to_bool(true)) {
00443 games_.back().have_era = true;
00444 } else {
00445 games_.back().have_era = false;
00446 }
00447 games_.back().map_info = vgettext("Unknown era: $era_id", symbols);
00448 verified = false;
00449 }
00450 } else {
00451 games_.back().map_info = _("Unknown era");
00452 verified = false;
00453 }
00454 games_.back().map_data = game["map_data"].str();
00455 if(games_.back().map_data.empty()) {
00456 games_.back().map_data = read_map(game["map"]);
00457 }
00458
00459 if(! games_.back().map_data.empty()) {
00460 try {
00461 std::vector<minimap_cache_item>::iterator i;
00462 bool found = false;
00463 for(i = minimap_cache.begin(); i != minimap_cache.end() && !found; ++i) {
00464 if (i->map_data == games_.back().map_data) {
00465 found = true;
00466 games_.back().map_info_size = i->map_info_size;
00467 games_.back().mini_map = i->mini_map;
00468 }
00469 }
00470 if (!found) {
00471
00472 gamemap map(game_config, games_.back().map_data);
00473 games_.back().mini_map = image::getMinimap(minimap_size_, minimap_size_, map, 0);
00474 games_.back().map_info_size = str_cast(map.w()) + utils::unicode_multiplication_sign
00475 + str_cast(map.h());
00476 }
00477 games_.back().map_info += " — " + games_.back().map_info_size;
00478 } catch (incorrect_map_format_error &e) {
00479 ERR_CF << "illegal map: " << e.message << '\n';
00480 verified = false;
00481 } catch(twml_exception& e) {
00482 ERR_CF << "map could not be loaded: " << e.dev_message << '\n';
00483 verified = false;
00484 }
00485 } else {
00486 games_.back().map_info += " — ??×??";
00487 }
00488 games_.back().map_info += " ";
00489 if (!game["mp_scenario"].empty())
00490 {
00491
00492 const config *level_cfg = &game_config.find_child("multiplayer", "id", game["mp_scenario"]);
00493 if (!*level_cfg) {
00494
00495 level_cfg = &game_config.find_child("generic_multiplayer", "id", game["mp_scenario"]);
00496 }
00497 if (*level_cfg) {
00498 games_.back().map_info += (*level_cfg)["name"].str();
00499
00500
00501
00502 if (map_hashes_ && !games_.back().reloaded) {
00503 std::string hash = game["hash"];
00504 bool hash_found = false;
00505 foreach (const config::attribute &i, map_hashes_.attribute_range()) {
00506 if (i.first == game["mp_scenario"] && i.second == hash) {
00507 hash_found = true;
00508 break;
00509 }
00510 }
00511 if(!hash_found) {
00512 games_.back().map_info += " — ";
00513 games_.back().map_info += _("Remote scenario");
00514 verified = false;
00515 }
00516 }
00517 } else {
00518 utils::string_map symbols;
00519 symbols["scenario_id"] = game["mp_scenario"];
00520 games_.back().map_info += vgettext("Unknown scenario: $scenario_id", symbols);
00521 verified = false;
00522 }
00523 } else {
00524 games_.back().map_info += _("Unknown scenario");
00525 verified = false;
00526 }
00527 if (games_.back().reloaded) {
00528 games_.back().map_info += " — ";
00529 games_.back().map_info += _("Reloaded game");
00530 verified = false;
00531 }
00532 games_.back().id = game["id"].str();
00533 games_.back().name = game["name"].str();
00534 std::string turn = game["turn"];
00535 std::string slots = game["slots"];
00536 games_.back().vacant_slots = lexical_cast_default<size_t>(slots, 0);
00537 games_.back().current_turn = 0;
00538 if (!turn.empty()) {
00539 games_.back().started = true;
00540 int index = turn.find_first_of('/');
00541 if (index > -1){
00542 const std::string current_turn = turn.substr(0, index);
00543 games_.back().current_turn = lexical_cast<unsigned int>(current_turn);
00544 }
00545 games_.back().status = _("Turn ") + turn;
00546 } else {
00547 games_.back().started = false;
00548 if (games_.back().vacant_slots > 0) {
00549 games_.back().status = std::string(_n("Vacant Slot:", "Vacant Slots:",
00550 games_.back().vacant_slots)) + " " + slots;
00551 if (games_.back().password_required) {
00552 games_.back().status += std::string(" (") + std::string(_("Password Required")) + ")";
00553 }
00554 }
00555 }
00556
00557 games_.back().use_map_settings = game["mp_use_map_settings"].to_bool();
00558 games_.back().gold = game["mp_village_gold"].str();
00559 if (game["mp_fog"].to_bool()) {
00560 games_.back().vision = _("Fog");
00561 games_.back().fog = true;
00562 if (game["mp_shroud"].to_bool()) {
00563 games_.back().vision += "/";
00564 games_.back().vision += _("Shroud");
00565 games_.back().shroud = true;
00566 } else {
00567 games_.back().shroud = false;
00568 }
00569 } else if (game["mp_shroud"].to_bool()) {
00570 games_.back().vision = _("Shroud");
00571 games_.back().fog = false;
00572 games_.back().shroud = true;
00573 } else {
00574 games_.back().vision = _("none");
00575 games_.back().fog = false;
00576 games_.back().shroud = false;
00577 }
00578 if (game["mp_countdown"].to_bool()) {
00579 games_.back().time_limit = game["mp_countdown_init_time"].str() + " / +"
00580 + game["mp_countdown_turn_bonus"].str() + " "
00581 + game["mp_countdown_action_bonus"].str();
00582 } else {
00583 games_.back().time_limit = "";
00584 }
00585 games_.back().xp = game["experience_modifier"].str() + "%";
00586 games_.back().observers = game["observer"].to_bool(true);
00587 games_.back().shuffle_sides = game["shuffle_sides"].to_bool(true);
00588 games_.back().verified = verified;
00589
00590
00591 if(preferences::fi_invert() ? game_matches_filter(games_.back(), cfg) : !game_matches_filter(games_.back(), cfg)) games_.pop_back();
00592 }
00593 set_full_size(games_.size());
00594 set_shown_size(inner_location().h / row_height());
00595
00596
00597 if (!selected_game.empty()) {
00598 for (unsigned int i=0; i < games_.size(); i++) {
00599 if (games_[i].id == selected_game) {
00600 selected_ = i;
00601 break;
00602 }
00603 }
00604 }
00605 if(selected_ >= games_.size())
00606 selected_ = std::max<long>(static_cast<long>(games_.size()) - 1, 0);
00607
00608 if (scrolled_to_max) {
00609 set_position(get_max_position());
00610 } else {
00611
00612 if (selection_visible && (visible_range_.first > selected_
00613 || visible_range_.second < selected_)) {
00614 set_position(selected_);
00615 }
00616 }
00617 scroll(get_position());
00618 set_dirty();
00619 }
00620
00621 void gamebrowser::select_game(const std::string& id) {
00622 if (id.empty()) return;
00623
00624 for (unsigned int i=0; i < games_.size(); i++) {
00625 if (games_[i].id == id) {
00626 selected_ = i;
00627 break;
00628 }
00629 }
00630 adjust_position(selected_);
00631 set_dirty();
00632 }
00633
00634 bool gamebrowser::game_matches_filter(const game_item& i, const config& cfg) {
00635
00636 if(!preferences::filter_lobby()) return true;
00637
00638 if(preferences::fi_vacant_slots() && i.vacant_slots == 0) return false;
00639
00640 if(preferences::fi_friends_in_game()) {
00641 bool found_friend = false;
00642 foreach(const config &user, cfg.child_range("user")) {
00643 if(preferences::is_friend(user["name"]) && user["game_id"] == i.id) {
00644 found_friend = true;
00645 break;
00646 }
00647 }
00648 if(!found_friend) return false;
00649 }
00650
00651 if(!preferences::fi_text().empty()) {
00652 bool found_match = true;
00653 foreach(const std::string& search_string, utils::split(preferences::fi_text(), ' ', utils::STRIP_SPACES)) {
00654 if(std::search(i.map_info.begin(), i.map_info.end(), search_string.begin(), search_string.end(), chars_equal_insensitive) == i.map_info.end() &&
00655 std::search(i.name.begin(), i.name.end(), search_string.begin(), search_string.end(), chars_equal_insensitive) == i.name.end()) {
00656 found_match = false;
00657 break;
00658 }
00659 }
00660 if(!found_match) return false;
00661 }
00662
00663 return true;
00664 }
00665
00666 lobby::lobby_sorter::lobby_sorter(const config& cfg) : cfg_(cfg)
00667 {
00668 set_alpha_sort(1);
00669 }
00670
00671 bool lobby::lobby_sorter::column_sortable(int column) const
00672 {
00673 switch(column)
00674 {
00675 case MAP_COLUMN:
00676 case STATUS_COLUMN:
00677 return true;
00678 default:
00679 return basic_sorter::column_sortable(column);
00680 }
00681 }
00682
00683 bool lobby::lobby_sorter::less(int column, const gui::menu::item& row1, const gui::menu::item& row2) const
00684 {
00685 const config &list = cfg_.child("gamelist");
00686 if (!list) {
00687 return false;
00688 }
00689
00690 size_t nb = list.child_count("game");
00691 if(row1.id >= nb || row2.id >= nb) {
00692 return false;
00693 }
00694
00695 config::const_child_iterator gi = list.child_range("game").first, gs = gi;
00696 std::advance(gi, row1.id);
00697 const config &game1 = *gi;
00698 gi = gs;
00699 std::advance(gi, row2.id);
00700 const config &game2 = *gi;
00701
00702 if(column == MAP_COLUMN) {
00703 size_t mapsize1 = game1["map_data"].str().size();
00704 if(mapsize1 == 0) {
00705 mapsize1 = game1["map"].str().size();
00706 }
00707
00708 size_t mapsize2 = game2["map_data"].str().size();
00709 if(mapsize2 == 0) {
00710 mapsize2 = game2["map"].str().size();
00711 }
00712
00713 return mapsize1 < mapsize2;
00714
00715 } else if(column == STATUS_COLUMN) {
00716 int nslots1 = game1["slots"].to_int();
00717 int nslots2 = game2["slots"].to_int();
00718
00719 int turn1 = game1["turn"].to_int();
00720 int turn2 = game2["turn"].to_int();
00721
00722 if(nslots1 > nslots2) {
00723 return true;
00724 } else if(nslots1 < nslots2) {
00725 return false;
00726 } else {
00727 return turn1 < turn2;
00728 }
00729 }
00730
00731 return basic_sorter::less(column,row1,row2);
00732 }
00733
00734 lobby::lobby(game_display& disp, const config& cfg, chat& c, config& gamelist) :
00735 mp::ui(disp, _("Game Lobby"), cfg, c, gamelist),
00736
00737 game_vacant_slots_(),
00738 game_observers_(),
00739
00740 observe_game_(disp.video(), _("Observe Game")),
00741 join_game_(disp.video(), _("Join Game")),
00742 create_game_(disp.video(), _("Create Game")),
00743 skip_replay_(disp.video(), _("Quick replays"), gui::button::TYPE_CHECK),
00744 game_preferences_(disp.video(), _("Preferences")),
00745 quit_game_(disp.video(), _("Quit")),
00746 apply_filter_(disp.video(), _("Apply filter"), gui::button::TYPE_CHECK),
00747 invert_filter_(disp.video(), _("Invert"), gui::button::TYPE_CHECK),
00748 vacant_slots_(disp.video(), _("Vacant slots"), gui::button::TYPE_CHECK),
00749 friends_in_game_(disp.video(), _("Friends in game"), gui::button::TYPE_CHECK),
00750 filter_label_(disp.video(), _("Search:")),
00751 filter_text_(disp.video(), 150),
00752 last_selected_game_(-1), sorter_(gamelist),
00753 games_menu_(disp.video(),cfg.child("multiplayer_hashes")),
00754 minimaps_(),
00755 search_string_(preferences::fi_text())
00756 {
00757 skip_replay_.set_check(preferences::skip_mp_replay());
00758 skip_replay_.set_help_string(_("Skip quickly to the active turn when observing"));
00759
00760 apply_filter_.set_check(preferences::filter_lobby());
00761 apply_filter_.set_help_string(_("Enable the games filter. If unchecked all games are shown, regardless of any filter."));
00762
00763 invert_filter_.set_check(preferences::fi_invert());
00764 invert_filter_.set_help_string(_("Show all games that do *not* match your filter. Useful for hiding games you are not interested in."));
00765 invert_filter_.enable(apply_filter_.checked());
00766
00767 vacant_slots_.set_check(preferences::fi_vacant_slots());
00768 vacant_slots_.set_help_string(_("Only show games that have at least one vacant slot"));
00769 vacant_slots_.enable(apply_filter_.checked());
00770
00771 friends_in_game_.set_check(preferences::fi_friends_in_game());
00772 friends_in_game_.set_help_string(_("Only show games that are played or observed by at least one of your friends"));
00773 friends_in_game_.enable(apply_filter_.checked());
00774
00775 filter_label_.enable(apply_filter_.checked());
00776
00777 filter_text_.set_text(search_string_);
00778 filter_text_.set_help_string(_("Only show games whose title or description contain the entered text"));
00779 filter_text_.set_editable(apply_filter_.checked());
00780
00781 gamelist_updated();
00782 sound::play_music_repeatedly(game_config::lobby_music);
00783 }
00784
00785 void lobby::hide_children(bool hide)
00786 {
00787 ui::hide_children(hide);
00788
00789 games_menu_.hide(hide);
00790 observe_game_.hide(hide);
00791 join_game_.hide(hide);
00792 create_game_.hide(hide);
00793 skip_replay_.hide(hide);
00794 game_preferences_.hide(hide);
00795 quit_game_.hide(hide);
00796 apply_filter_.hide(hide);
00797 invert_filter_.hide(hide);
00798 vacant_slots_.hide(hide);
00799 friends_in_game_.hide(hide);
00800 filter_label_.hide(hide);
00801 filter_text_.hide(hide);
00802 }
00803
00804 void lobby::layout_children(const SDL_Rect& rect)
00805 {
00806 ui::layout_children(rect);
00807
00808 int btn_space = 5;
00809 int xborder = 10;
00810 int yborder = 7;
00811
00812
00813 join_game_.set_location(xscale(xborder), yscale(yborder));
00814 observe_game_.set_location(join_game_.location().x + join_game_.location().w + btn_space, yscale(yborder));
00815 create_game_.set_location(observe_game_.location().x + observe_game_.location().w + btn_space, yscale(yborder));
00816
00817
00818 quit_game_.set_location(xscale(xscale_base - xborder) - quit_game_.location().w, yscale(yborder));
00819
00820
00821 int space = (quit_game_.location().x - create_game_.location().x - create_game_.location().w
00822 - skip_replay_.location().w - game_preferences_.location().w - btn_space) / 2;
00823 if (space < btn_space) space = btn_space;
00824 skip_replay_.set_location(create_game_.location().x + create_game_.location().w + space, yscale(yborder));
00825 game_preferences_.set_location(quit_game_.location().x - game_preferences_.location().w - space, yscale(yborder));
00826
00827 games_menu_.set_location(client_area().x, client_area().y + title().height());
00828 games_menu_.set_measurements(client_area().w, client_area().h
00829 - title().height() - gui::ButtonVPadding
00830 - apply_filter_.location().h
00831 );
00832
00833 apply_filter_.set_location(client_area().x, games_menu_.location().y + games_menu_.location().h + gui::ButtonVPadding);
00834 invert_filter_.set_location(client_area().x + apply_filter_.location().w + btn_space, games_menu_.location().y + games_menu_.location().h + gui::ButtonVPadding);
00835 vacant_slots_.set_location(client_area().w - apply_filter_.location().w, games_menu_.location().y + games_menu_.location().h + gui::ButtonVPadding);
00836 friends_in_game_.set_location(vacant_slots_.location().x - friends_in_game_.location().w - btn_space, games_menu_.location().y + games_menu_.location().h + gui::ButtonVPadding);
00837 filter_text_.set_location(friends_in_game_.location().x - filter_text_.location().w - btn_space * 4, games_menu_.location().y + games_menu_.location().h + gui::ButtonVPadding);
00838 filter_label_.set_location(filter_text_.location().x - filter_label_.location().w - btn_space, games_menu_.location().y + games_menu_.location().h + gui::ButtonVPadding
00839 + (apply_filter_.location().h - filter_label_.location().h) / 2);
00840
00841 }
00842
00843 void lobby::gamelist_updated(bool silent)
00844 {
00845 ui::gamelist_updated(silent);
00846 const config &list = gamelist().child("gamelist");
00847 if (!list) {
00848
00849 return;
00850 }
00851 games_menu_.set_game_items(gamelist(), game_config());
00852 join_game_.enable(games_menu_.selection_is_joinable());
00853 observe_game_.enable(games_menu_.selection_is_observable());
00854 }
00855
00856 void lobby::process_event()
00857 {
00858 join_game_.enable(games_menu_.selection_is_joinable());
00859 observe_game_.enable(games_menu_.selection_is_observable());
00860
00861 const bool observe = (observe_game_.pressed() || (games_menu_.selected() && !games_menu_.selection_is_joinable())) && games_menu_.selection_is_observable();
00862 const bool join = (join_game_.pressed() || games_menu_.selected()) && games_menu_.selection_is_joinable();
00863 games_menu_.reset_selection();
00864 preferences::set_skip_mp_replay(skip_replay_.checked());
00865 playmp_controller::set_replay_last_turn(0);
00866 preferences::set_message_private(false);
00867
00868 int selected_game = games_menu_.selection();
00869 if (selected_game != last_selected_game_) {
00870 if (games_menu_.empty()) {
00871 set_selected_game("");
00872 } else {
00873 set_selected_game(games_menu_.selected_game().id);
00874 }
00875 ui::gamelist_updated();
00876 last_selected_game_ = selected_game;
00877 }
00878
00879 if(selected_user_changed()) {
00880 set_selected_user_changed(false);
00881 games_menu_.select_game(get_selected_user_game());
00882 }
00883
00884 if(join || observe) {
00885 const int selected = games_menu_.selection();
00886 if(!games_menu_.empty() && selected >= 0) {
00887 gamebrowser::game_item game = games_menu_.selected_game();
00888
00889 std::string password;
00890 if(join && game.password_required) {
00891 const int res = gui::show_dialog(disp_, NULL, _("Password Required"),
00892 _("Joining this game requires a password."),
00893 gui::OK_CANCEL, NULL, NULL, _("Password: "), &password);
00894 if(res != 0) {
00895 return;
00896 }
00897 }
00898
00899 config response;
00900 config& join = response.add_child("join");
00901 join["id"] = game.id;
00902 join["observe"] = observe;
00903
00904 if(!password.empty()) {
00905 join["password"] = password;
00906 }
00907 network::send_data(response, 0);
00908
00909 if(observe) {
00910 if (game.started){
00911 playmp_controller::set_replay_last_turn(game.current_turn);
00912 }
00913 set_result(OBSERVE);
00914 } else {
00915 set_result(JOIN);
00916 }
00917 }
00918 return;
00919 }
00920
00921 if(create_game_.pressed()) {
00922 set_result(CREATE);
00923 return;
00924 }
00925
00926 if(game_preferences_.pressed()) {
00927 set_result(PREFERENCES);
00928 return;
00929 }
00930
00931 if(quit_game_.pressed()) {
00932 recorder.set_skip(false);
00933 set_result(QUIT);
00934 return;
00935 }
00936
00937 if(apply_filter_.pressed()) {
00938 preferences::set_filter_lobby(apply_filter_.checked());
00939 invert_filter_.enable(apply_filter_.checked());
00940 vacant_slots_.enable(apply_filter_.checked());
00941 friends_in_game_.enable(apply_filter_.checked());
00942 filter_label_.enable(apply_filter_.checked());
00943
00944
00945
00946
00947
00948
00949 filter_text_.set_editable(apply_filter_.checked());
00950 gamelist_updated();
00951 return;
00952 }
00953
00954 if(invert_filter_.pressed()) {
00955 preferences::set_fi_invert(invert_filter_.checked());
00956 gamelist_updated();
00957 return;
00958 }
00959
00960 if(vacant_slots_.pressed()) {
00961 preferences::set_fi_vacant_slots(vacant_slots_.checked());
00962 gamelist_updated();
00963 return;
00964 }
00965
00966 if(friends_in_game_.pressed()) {
00967 preferences::set_fi_friends_in_game(friends_in_game_.checked());
00968 gamelist_updated();
00969 return;
00970 }
00971
00972 if(search_string_ != filter_text_.text()) {
00973 search_string_ = filter_text_.text();
00974 preferences::set_fi_text(search_string_);
00975 gamelist_updated();
00976 }
00977
00978 }
00979
00980 void lobby::process_network_data(const config& data, const network::connection sock)
00981 {
00982 ui::process_network_data(data, sock);
00983
00984
00985 last_selected_game_ = -1;
00986 }
00987
00988 }
00989