dialogs.cpp

Go to the documentation of this file.
00001 /* $Id: dialogs.cpp 54210 2012-05-18 20:11:11Z shadowmaster $ */
00002 /*
00003    Copyright (C) 2003 - 2012 by David White <dave@whitevine.net>
00004    Part of the Battle for Wesnoth Project http://www.wesnoth.org/
00005 
00006    This program is free software; you can redistribute it and/or modify
00007    it under the terms of the GNU General Public License as published by
00008    the Free Software Foundation; either version 2 of the License, or
00009    (at your option) any later version.
00010    This program is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY.
00012 
00013    See the COPYING file for more details.
00014 */
00015 
00016 /**
00017  * @file
00018  * Various dialogs: advance_unit, show_objectives, save+load game, network::connection.
00019  */
00020 
00021 #include "global.hpp"
00022 
00023 #include "actions.hpp"
00024 #include "dialogs.hpp"
00025 #include "foreach.hpp"
00026 #include "game_events.hpp"
00027 #include "game_display.hpp"
00028 #include "game_preferences.hpp"
00029 #include "gui/dialogs/game_delete.hpp"
00030 #include "gettext.hpp"
00031 #include "help.hpp"
00032 #include "language.hpp"
00033 #include "log.hpp"
00034 #include "map.hpp"
00035 #include "map_exception.hpp"
00036 #include "marked-up_text.hpp"
00037 #include "menu_events.hpp"
00038 #include "mouse_handler_base.hpp"
00039 #include "minimap.hpp"
00040 #include "replay.hpp"
00041 #include "resources.hpp"
00042 #include "savegame.hpp"
00043 #include "thread.hpp"
00044 #include "unit_helper.hpp"
00045 #include "wml_separators.hpp"
00046 #include "widgets/progressbar.hpp"
00047 #include "wml_exception.hpp"
00048 #include "formula_string_utils.hpp"
00049 #include "gui/dialogs/game_save.hpp"
00050 #include "gui/dialogs/transient_message.hpp"
00051 
00052 
00053 //#ifdef _WIN32
00054 //#include "locale.h"
00055 //#endif
00056 
00057 #include <clocale>
00058 
00059 static lg::log_domain log_engine("engine");
00060 #define LOG_NG LOG_STREAM(info, log_engine)
00061 #define ERR_NG LOG_STREAM(err, log_engine)
00062 
00063 static lg::log_domain log_display("display");
00064 #define LOG_DP LOG_STREAM(info, log_display)
00065 
00066 #define ERR_G  LOG_STREAM(err, lg::general)
00067 
00068 static lg::log_domain log_config("config");
00069 #define ERR_CF LOG_STREAM(err, log_config)
00070 
00071 namespace dialogs
00072 {
00073 
00074 int advance_unit_dialog(const map_location &loc)
00075 {
00076     unit_map::iterator u = resources::units->find(loc);
00077 
00078     const std::vector<std::string>& options = u->advances_to();
00079 
00080     std::vector<std::string> lang_options;
00081 
00082     std::vector<unit> sample_units;
00083     for(std::vector<std::string>::const_iterator op = options.begin(); op != options.end(); ++op) {
00084         sample_units.push_back(::get_advanced_unit(*u, *op));
00085         const unit& type = sample_units.back();
00086 
00087 #ifdef LOW_MEM
00088         lang_options.push_back(IMAGE_PREFIX
00089                 + static_cast<std::string const &>(type.absolute_image())
00090                 + COLUMN_SEPARATOR + type.type_name());
00091 #else
00092         lang_options.push_back(IMAGE_PREFIX + type.absolute_image() + u->image_mods() + COLUMN_SEPARATOR + type.type_name());
00093 #endif
00094         preferences::encountered_units().insert(*op);
00095     }
00096 
00097     bool always_display = false;
00098     foreach (const config &mod, u->get_modification_advances())
00099     {
00100         if (mod["always_display"].to_bool()) always_display = true;
00101         sample_units.push_back(::get_advanced_unit(*u, u->type_id()));
00102         sample_units.back().add_modification("advance", mod);
00103         const unit& type = sample_units.back();
00104         if (!mod["image"].empty()) {
00105             lang_options.push_back(IMAGE_PREFIX + mod["image"].str() + COLUMN_SEPARATOR + mod["description"].str());
00106         } else {
00107 #ifdef LOW_MEM
00108             lang_options.push_back(IMAGE_PREFIX
00109                     + static_cast<std::string const &>(type.absolute_image())
00110                     + COLUMN_SEPARATOR + mod["description"].str());
00111 #else
00112             lang_options.push_back(IMAGE_PREFIX + type.absolute_image() + u->image_mods() + COLUMN_SEPARATOR + mod["description"].str());
00113 #endif
00114         }
00115     }
00116 
00117     assert(!lang_options.empty());
00118 
00119     if (lang_options.size() > 1 || always_display)
00120     {
00121         units_list_preview_pane unit_preview(sample_units);
00122         std::vector<gui::preview_pane*> preview_panes;
00123         preview_panes.push_back(&unit_preview);
00124 
00125         gui::dialog advances = gui::dialog(*resources::screen,
00126                       _("Advance Unit"),
00127                               _("What should our victorious unit become?"),
00128                               gui::OK_ONLY);
00129         advances.set_menu(lang_options);
00130         advances.set_panes(preview_panes);
00131         return advances.show();
00132     }
00133     return 0;
00134 }
00135 
00136 void advance_unit(const map_location &loc, bool random_choice, bool add_replay_event)
00137 {
00138     unit_map::iterator u = resources::units->find(loc);
00139     if(!unit_helper::will_certainly_advance(u)) {
00140         return;
00141     }
00142 
00143     LOG_DP << "advance_unit: " << u->type_id() << " (advances: " << u->advances()
00144         << " XP: " <<u->experience() << '/' << u->max_experience() << ")\n";
00145 
00146     int res;
00147 
00148     if (random_choice) {
00149         res = rand() % unit_helper::number_of_possible_advances(*u);
00150     } else {
00151         res = advance_unit_dialog(loc);
00152     }
00153 
00154     if(add_replay_event) {
00155         recorder.add_advancement(loc);
00156     }
00157 
00158     config choice_cfg;
00159     choice_cfg["value"] = res;
00160     recorder.user_input("choose", choice_cfg);
00161 
00162     LOG_DP << "animating advancement...\n";
00163     animate_unit_advancement(loc, size_t(res));
00164 
00165     // In some rare cases the unit can have enough XP to advance again,
00166     // so try to do that.
00167     // Make sure that we don't enter an infinite level loop.
00168     u = resources::units->find(loc);
00169     if (u != resources::units->end()) {
00170         // Level 10 unit gives 80 XP and the highest mainline is level 5
00171         if (u->experience() < 81) {
00172             // For all leveling up we have to add advancement to replay here because replay
00173             // doesn't handle cascading advancement since it just calls animate_unit_advancement().
00174             advance_unit(loc, random_choice, true);
00175         } else {
00176             ERR_CF << "Unit has an too high amount of " << u->experience()
00177                 << " XP left, cascade leveling disabled\n";
00178         }
00179     } else {
00180         ERR_NG << "Unit advanced no longer exists\n";
00181     }
00182 }
00183 
00184 bool animate_unit_advancement(const map_location &loc, size_t choice, const bool &fire_event)
00185 {
00186     const events::command_disabler cmd_disabler;
00187 
00188     unit_map::iterator u = resources::units->find(loc);
00189     if (u == resources::units->end()) {
00190         LOG_DP << "animate_unit_advancement suppressed: invalid unit\n";
00191         return false;
00192     } else if (!u->advances()) {
00193         LOG_DP << "animate_unit_advancement suppressed: unit does not advance\n";
00194         return false;
00195     }
00196 
00197     const std::vector<std::string>& options = u->advances_to();
00198     std::vector<config> mod_options = u->get_modification_advances();
00199 
00200     if(choice >= options.size() + mod_options.size()) {
00201         LOG_DP << "animate_unit_advancement suppressed: invalid option\n";
00202         return false;
00203     }
00204 
00205     // When the unit advances, it fades to white, and then switches
00206     // to the new unit, then fades back to the normal color
00207 
00208     if (!resources::screen->video().update_locked()) {
00209         unit_animator animator;
00210         bool with_bars = true;
00211         animator.add_animation(&*u,"levelout", u->get_location(), map_location(), 0, with_bars);
00212         animator.start_animations();
00213         animator.wait_for_end();
00214     }
00215 
00216     if(choice < options.size()) {
00217         // chosen_unit is not a reference, since the unit may disappear at any moment.
00218         std::string chosen_unit = options[choice];
00219 		::advance_unit(loc, chosen_unit, fire_event);
00220     } else {
00221         unit amla_unit(*u);
00222         const config &mod_option = mod_options[choice - options.size()];
00223 
00224         if(fire_event)
00225         {
00226             LOG_NG << "firing advance event (AMLA)\n";
00227             game_events::fire("advance",loc);
00228         }
00229 
00230         amla_unit.set_experience(amla_unit.experience() - amla_unit.max_experience());
00231         amla_unit.add_modification("advance",mod_option);
00232         resources::units->replace(loc, amla_unit);
00233 
00234         if(fire_event)
00235         {
00236             LOG_NG << "firing post_advance event (AMLA)\n";
00237             game_events::fire("post_advance",loc);
00238         }
00239 
00240     }
00241 
00242     u = resources::units->find(loc);
00243     resources::screen->invalidate_unit();
00244 
00245     if (u != resources::units->end() && !resources::screen->video().update_locked()) {
00246         unit_animator animator;
00247         animator.add_animation(&*u, "levelin", u->get_location(), map_location(), 0, true);
00248         animator.start_animations();
00249         animator.wait_for_end();
00250         animator.set_all_standing();
00251         resources::screen->invalidate(loc);
00252         resources::screen->draw();
00253         events::pump();
00254     }
00255 
00256     resources::screen->invalidate_all();
00257     resources::screen->draw();
00258 
00259     return true;
00260 }
00261 
00262 void show_objectives(const config &level, const std::string &objectives)
00263 {
00264     static const std::string no_objectives(_("No objectives available"));
00265     gui2::show_transient_message(resources::screen->video(), level["name"],
00266         (objectives.empty() ? no_objectives : objectives), "", true);
00267 }
00268 
00269 namespace {
00270 
00271 /** Class to handle deleting a saved game. */
00272 class delete_save : public gui::dialog_button_action
00273 {
00274 public:
00275     delete_save(display& disp, gui::filter_textbox& filter, std::vector<savegame::save_info>& saves) :
00276         disp_(disp), saves_(saves), filter_(filter) {}
00277 private:
00278     gui::dialog_button_action::RESULT button_pressed(int menu_selection);
00279 
00280     display& disp_;
00281     std::vector<savegame::save_info>& saves_;
00282     gui::filter_textbox& filter_;
00283 };
00284 
00285 gui::dialog_button_action::RESULT delete_save::button_pressed(int menu_selection)
00286 {
00287     const size_t index = size_t(filter_.get_index(menu_selection));
00288     if(index < saves_.size()) {
00289 
00290         // See if we should ask the user for deletion confirmation
00291         if(preferences::ask_delete_saves()) {
00292             if(!gui2::tgame_delete::execute(disp_.video())) {
00293                 return gui::CONTINUE_DIALOG;
00294             }
00295         }
00296 
00297         // Remove the item from filter_textbox memory
00298         filter_.delete_item(menu_selection);
00299 
00300         // Delete the file
00301         savegame::delete_game(saves_[index].name());
00302 
00303         // Remove it from the list of saves
00304         saves_.erase(saves_.begin() + index);
00305 
00306         return gui::DELETE_ITEM;
00307     } else {
00308         return gui::CONTINUE_DIALOG;
00309     }
00310 }
00311 
00312 static const int save_preview_border = 10;
00313 
00314 class save_preview_pane : public gui::preview_pane
00315 {
00316 public:
00317     save_preview_pane(CVideo &video, const config& game_config, gamemap* map,
00318             const std::vector<savegame::save_info>& info,
00319             const gui::filter_textbox& textbox) :
00320         gui::preview_pane(video),
00321         game_config_(&game_config),
00322         map_(map), info_(&info),
00323         index_(0),
00324         map_cache_(),
00325         textbox_(textbox)
00326     {
00327         set_measurements(std::min<int>(200,video.getx()/4),
00328                  std::min<int>(400,video.gety() * 4/5));
00329     }
00330 
00331     void draw_contents();
00332     void set_selection(int index) {
00333         int res = textbox_.get_index(index);
00334         index_ = (res >= 0) ? res : index_;
00335         set_dirty();
00336     }
00337 
00338     bool left_side() const { return true; }
00339 
00340 private:
00341     const config* game_config_;
00342     gamemap* map_;
00343     const std::vector<savegame::save_info>* info_;
00344     int index_;
00345     std::map<std::string,surface> map_cache_;
00346     const gui::filter_textbox& textbox_;
00347 };
00348 
00349 void save_preview_pane::draw_contents()
00350 {
00351     surface screen = video().getSurface();
00352 
00353     SDL_Rect const &loc = location();
00354     const SDL_Rect area = create_rect(loc.x + save_preview_border
00355             , loc.y + save_preview_border
00356             , loc.w - save_preview_border * 2
00357             , loc.h - save_preview_border * 2);
00358 
00359     const clip_rect_setter clipper(screen, &area);
00360 
00361     int ypos = area.y;
00362 
00363     bool have_leader_image = false;
00364     const config& summary = ((*info_)[index_]).summary();
00365     const std::string& leader_image = summary["leader_image"].str();
00366 
00367     if(!leader_image.empty() && image::exists(leader_image))
00368     {
00369 #ifdef LOW_MEM
00370         const surface& image(image::get_image(leader_image));
00371 #else
00372         // NOTE: assuming magenta for TC here. This is what's used in all of
00373         // mainline, so the compromise should be good enough until we add more
00374         // summary fields to help with this and deciding the side color range.
00375         const surface& image(image::get_image(leader_image + "~RC(magenta>1)"));
00376 #endif
00377 
00378         have_leader_image = !image.null();
00379 
00380         if(have_leader_image) {
00381             SDL_Rect image_rect = create_rect(area.x, area.y, image->w, image->h);
00382             ypos += image_rect.h + save_preview_border;
00383 
00384             sdl_blit(image,NULL,screen,&image_rect);
00385         }
00386     }
00387 
00388     std::string map_data = summary["map_data"];
00389     if(map_data.empty()) {
00390         const config &scenario = game_config_->find_child(summary["campaign_type"], "id", summary["scenario"]);
00391         if (scenario && !scenario.find_child("side", "shroud", "yes")) {
00392             map_data = scenario["map_data"].str();
00393             if (map_data.empty() && scenario.has_attribute("map")) {
00394                 try {
00395                     map_data = read_map(scenario["map"]);
00396                 } catch(io_exception& e) {
00397                     ERR_G << "could not read map '" << scenario["map"] << "': " << e.what() << '\n';
00398                 }
00399             }
00400         }
00401     }
00402 
00403     surface map_surf(NULL);
00404 
00405     if(map_data.empty() == false) {
00406         const std::map<std::string,surface>::const_iterator itor = map_cache_.find(map_data);
00407         if(itor != map_cache_.end()) {
00408             map_surf = itor->second;
00409         } else if(map_ != NULL) {
00410             try {
00411                 const int minimap_size = 100;
00412                 map_->read(map_data);
00413 
00414                 map_surf = image::getMinimap(minimap_size, minimap_size, *map_);
00415                 if(map_surf != NULL) {
00416                     map_cache_.insert(std::pair<std::string,surface>(map_data,surface(map_surf)));
00417                 }
00418             } catch(incorrect_map_format_error& e) {
00419                 ERR_CF << "map could not be loaded: " << e.message << '\n';
00420             } catch(twml_exception& e) {
00421                 ERR_CF << "map could not be loaded: " << e.dev_message << '\n';
00422             }
00423         }
00424     }
00425 
00426     if(map_surf != NULL) {
00427         // Align the map to the left when the leader image is missing.
00428         const int map_x = have_leader_image ? area.x + area.w - map_surf->w : area.x;
00429         SDL_Rect map_rect = create_rect(map_x
00430                 , area.y
00431                 , map_surf->w
00432                 , map_surf->h);
00433 
00434         ypos = std::max<int>(ypos,map_rect.y + map_rect.h + save_preview_border);
00435         sdl_blit(map_surf,NULL,screen,&map_rect);
00436     }
00437 
00438     char time_buf[256] = {0};
00439     const savegame::save_info& save = (*info_)[index_];
00440     tm* tm_l = localtime(&save.modified());
00441     if (tm_l) {
00442         const size_t res = strftime(time_buf,sizeof(time_buf),
00443             (preferences::use_twelve_hour_clock_format() ? _("%a %b %d %I:%M %p %Y") : _("%a %b %d %H:%M %Y")),
00444             tm_l);
00445         if(res == 0) {
00446             time_buf[0] = 0;
00447         }
00448     } else {
00449         LOG_NG << "localtime() returned null for time " << save.modified() << ", save " << save.name();
00450     }
00451 
00452     std::stringstream str;
00453 
00454     str << font::BOLD_TEXT << font::NULL_MARKUP
00455         << (*info_)[index_].name() << '\n' << time_buf;
00456 
00457     const std::string& campaign_type = summary["campaign_type"];
00458     if (summary["corrupt"].to_bool()) {
00459         str << "\n" << _("#(Invalid)");
00460     } else if (!campaign_type.empty()) {
00461         str << "\n";
00462 
00463         if(campaign_type == "scenario") {
00464             const std::string campaign_id = summary["campaign"];
00465             const config *campaign = NULL;
00466             if (!campaign_id.empty()) {
00467                 if (const config &c = game_config_->find_child("campaign", "id", campaign_id))
00468                     campaign = &c;
00469             }
00470             utils::string_map symbols;
00471             if (campaign != NULL) {
00472                 symbols["campaign_name"] = (*campaign)["name"];
00473             } else {
00474                 // Fallback to nontranslatable campaign id.
00475                 symbols["campaign_name"] = "(" + campaign_id + ")";
00476             }
00477             str << vgettext("Campaign: $campaign_name", symbols);
00478 
00479             // Display internal id for debug purposes if we didn't above
00480             if (game_config::debug && (campaign != NULL)) {
00481                 str << '\n' << "(" << campaign_id << ")";
00482             }
00483         } else if(campaign_type == "multiplayer") {
00484             str << _("Multiplayer");
00485         } else if(campaign_type == "tutorial") {
00486             str << _("Tutorial");
00487         } else if(campaign_type == "test") {
00488             str << _("Test scenario");
00489         } else {
00490             str << campaign_type;
00491         }
00492 
00493         str << "\n";
00494 
00495         if (summary["replay"].to_bool() && !summary["snapshot"].to_bool(true)) {
00496             str << _("Replay");
00497         } else if (!summary["turn"].empty()) {
00498             str << _("Turn") << " " << summary["turn"];
00499         } else {
00500             str << _("Scenario start");
00501         }
00502 
00503         str << "\n" << _("Difficulty: ") << string_table[summary["difficulty"]];
00504         if(!summary["version"].empty()) {
00505             str << "\n" << _("Version: ") << summary["version"];
00506         }
00507     }
00508 
00509     font::draw_text(&video(), area, font::SIZE_SMALL, font::NORMAL_COLOR, str.str(), area.x, ypos, true);
00510 }
00511 
00512 std::string format_time_summary(time_t t)
00513 {
00514     time_t curtime = time(NULL);
00515     const struct tm* timeptr = localtime(&curtime);
00516     if(timeptr == NULL) {
00517         return "";
00518     }
00519 
00520     const struct tm current_time = *timeptr;
00521 
00522     timeptr = localtime(&t);
00523     if(timeptr == NULL) {
00524         return "";
00525     }
00526 
00527     const struct tm save_time = *timeptr;
00528 
00529     const char* format_string = _("%b %d %y");
00530 
00531     if(current_time.tm_year == save_time.tm_year) {
00532         const int days_apart = current_time.tm_yday - save_time.tm_yday;
00533         if(days_apart == 0) {
00534             // save is from today
00535             if(preferences::use_twelve_hour_clock_format() == false) {
00536                 format_string = _("%H:%M");
00537             }
00538             else {
00539                 format_string = _("%I:%M %p");
00540             }
00541         } else if(days_apart > 0 && days_apart <= current_time.tm_wday) {
00542             // save is from this week
00543             if(preferences::use_twelve_hour_clock_format() == false) {
00544                 format_string = _("%A, %H:%M");
00545             }
00546             else {
00547                 format_string = _("%A, %I:%M %p");
00548             }
00549         } else {
00550             // save is from current year
00551             format_string = _("%b %d");
00552         }
00553     } else {
00554         // save is from a different year
00555         format_string = _("%b %d %y");
00556     }
00557 
00558     char buf[40];
00559     const size_t res = strftime(buf,sizeof(buf),format_string,&save_time);
00560     if(res == 0) {
00561         buf[0] = 0;
00562     }
00563 
00564     return buf;
00565 }
00566 
00567 } // end anon namespace
00568 
00569 std::string load_game_dialog(display& disp, const config& game_config, bool* select_difficulty, bool* show_replay, bool* cancel_orders)
00570 {
00571     std::vector<savegame::save_info> games;
00572     {
00573         cursor::setter cur(cursor::WAIT);
00574         games = savegame::get_saves_list();
00575     }
00576 
00577     if(games.empty()) {
00578         gui2::show_transient_message(disp.video(),
00579                          _("No Saved Games"),
00580                  _("There are no saved games to load.\n\n(Games are saved automatically when you complete a scenario)"));
00581         return "";
00582     }
00583 
00584     const events::event_context context;
00585 
00586     std::vector<std::string> items;
00587     std::ostringstream heading;
00588     heading << HEADING_PREFIX << _("Name") << COLUMN_SEPARATOR << _("Date");
00589     items.push_back(heading.str());
00590     std::vector<savegame::save_info>::const_iterator i;
00591     for(i = games.begin(); i != games.end(); ++i) {
00592         std::string name = i->name();
00593         utils::truncate_as_wstring(name, std::min<size_t>(name.size(), 40));
00594 
00595         std::ostringstream str;
00596         str << name << COLUMN_SEPARATOR << format_time_summary(i->modified());
00597 
00598         items.push_back(str.str());
00599     }
00600 
00601     gamemap map_obj(game_config, "");
00602 
00603 
00604     gui::dialog lmenu(disp,
00605               _("Load Game"),
00606               "", gui::NULL_DIALOG);
00607     lmenu.set_basic_behavior(gui::OK_CANCEL);
00608 
00609     gui::menu::basic_sorter sorter;
00610     sorter.set_alpha_sort(0).set_id_sort(1);
00611     lmenu.set_menu(items, &sorter);
00612 
00613     gui::filter_textbox* filter = new gui::filter_textbox(disp.video(), _("Filter: "), items, items, 1, lmenu);
00614     lmenu.set_textbox(filter);
00615 
00616     save_preview_pane save_preview(disp.video(),game_config,&map_obj,games,*filter);
00617     lmenu.add_pane(&save_preview);
00618     // create an option for whether the replay should be shown or not
00619     if(show_replay != NULL) {
00620         lmenu.add_option(_("Show replay"), false,
00621             // use smallgui layout as default, otherwise the load screen glitches at low res!
00622             //game_config::small_gui ? gui::dialog::BUTTON_CHECKBOX : gui::dialog::BUTTON_STANDARD);
00623             gui::dialog::BUTTON_CHECKBOX_LEFT,
00624             _("Play the embedded replay from the saved game if applicable")
00625             );
00626     }
00627     if(cancel_orders != NULL) {
00628         lmenu.add_option(_("Cancel orders"), false,
00629             // use smallgui layout as default, otherwise the load screen glitches at low res!
00630             //game_config::small_gui ? gui::dialog::BUTTON_STANDARD : gui::dialog::BUTTON_EXTRA);
00631             gui::dialog::BUTTON_CHECKBOX_LEFT,
00632             _("Cancel any pending unit movements in the saved game")
00633             );
00634     }
00635     if(select_difficulty != NULL) {
00636         lmenu.add_option(_("Change difficulty"), false,
00637             gui::dialog::BUTTON_CHECKBOX,
00638             _("Change campaign difficulty before loading")
00639             );
00640     }
00641     lmenu.add_button(new gui::standard_dialog_button(disp.video(),_("OK"),0,false), gui::dialog::BUTTON_STANDARD);
00642     lmenu.add_button(new gui::standard_dialog_button(disp.video(),_("Cancel"),1,true), gui::dialog::BUTTON_STANDARD);
00643 
00644     delete_save save_deleter(disp,*filter,games);
00645     gui::dialog_button_info delete_button(&save_deleter,_("Delete Save"));
00646 
00647     lmenu.add_button(delete_button,
00648         // use smallgui layout as default, otherwise the load screen glitches at low res!
00649         //game_config::small_gui ? gui::dialog::BUTTON_HELP : gui::dialog::BUTTON_EXTRA);
00650         gui::dialog::BUTTON_HELP);
00651 
00652     int res = lmenu.show();
00653 
00654     if(res == -1)
00655         return "";
00656 
00657     res = filter->get_index(res);
00658     int option_index = 0;
00659     if(show_replay != NULL) {
00660       *show_replay = lmenu.option_checked(option_index++);
00661 
00662         const config& summary = games[res].summary();
00663         if (summary["replay"].to_bool() && !summary["snapshot"].to_bool(true)) {
00664             *show_replay = true;
00665         }
00666     }
00667     if (cancel_orders != NULL) {
00668         *cancel_orders = lmenu.option_checked(option_index++);
00669     }
00670     if (select_difficulty != NULL) {
00671         *select_difficulty = lmenu.option_checked(option_index++);
00672     }
00673 
00674     return games[res].name();
00675 }
00676 
00677 namespace {
00678     static const int unit_preview_border = 10;
00679 }
00680 
00681 unit_preview_pane::details::details() :
00682     image(),
00683     name(),
00684     type_name(),
00685     race(),
00686     level(0),
00687     alignment(),
00688     traits(),
00689     abilities(),
00690     hitpoints(0),
00691     max_hitpoints(0),
00692     experience(0),
00693     max_experience(0),
00694     hp_color(),
00695     xp_color(),
00696     movement_left(0),
00697     total_movement(0),
00698     attacks()
00699 {
00700 }
00701 
00702 unit_preview_pane::unit_preview_pane(const gui::filter_textbox *filter, TYPE type, bool on_left_side) :
00703     gui::preview_pane(resources::screen->video()), index_(0),
00704     details_button_(resources::screen->video(), _("Profile"),
00705                       gui::button::TYPE_PRESS,"lite_small", gui::button::MINIMUM_SPACE),
00706                       filter_(filter), weapons_(type == SHOW_ALL), left_(on_left_side)
00707 {
00708     unsigned w = font::relative_size(weapons_ ? 200 : 190);
00709 // advance test
00710     unsigned h = font::relative_size(weapons_ ? 440 : 140);
00711     set_measurements(w, h);
00712 }
00713 
00714 
00715 handler_vector unit_preview_pane::handler_members()
00716 {
00717     handler_vector h;
00718     h.push_back(&details_button_);
00719     return h;
00720 }
00721 
00722 bool unit_preview_pane::show_above() const
00723 {
00724     return !weapons_;
00725 }
00726 
00727 bool unit_preview_pane::left_side() const
00728 {
00729     return left_;
00730 }
00731 
00732 void unit_preview_pane::set_selection(int index)
00733 {
00734     index = std::min<int>(static_cast<int>(size())-1,index);
00735     if (filter_) {
00736         index = filter_->get_index(index);
00737     }
00738     if(index != index_) {
00739         index_ = index;
00740         set_dirty();
00741         if (index >= 0) {
00742             details_button_.set_dirty();
00743         }
00744     }
00745 }
00746 
00747 void unit_preview_pane::draw_contents()
00748 {
00749     if(index_ < 0 || index_ >= int(size())) {
00750         return;
00751     }
00752 
00753     const details det = get_details();
00754 
00755     const bool right_align = left_side();
00756 
00757     surface screen = video().getSurface();
00758 
00759     SDL_Rect const &loc = location();
00760     const SDL_Rect area = create_rect(loc.x + unit_preview_border
00761             , loc.y + unit_preview_border
00762             , loc.w - unit_preview_border * 2
00763             , loc.h - unit_preview_border * 2);
00764 
00765     const clip_rect_setter clipper(screen, &area);
00766 
00767     surface unit_image = det.image;
00768     if (!left_)
00769         unit_image = image::reverse_image(unit_image);
00770 
00771     SDL_Rect image_rect = create_rect(area.x, area.y, 0, 0);
00772 
00773     if(unit_image != NULL) {
00774         SDL_Rect rect = create_rect(
00775                   right_align
00776                     ? area.x
00777                     : area.x + area.w - unit_image->w
00778                 , area.y
00779                 , unit_image->w
00780                 , unit_image->h);
00781 
00782         sdl_blit(unit_image,NULL,screen,&rect);
00783         image_rect = rect;
00784     }
00785 
00786     // Place the 'unit profile' button
00787     const SDL_Rect button_loc = create_rect(
00788               right_align
00789                 ? area.x
00790                 : area.x + area.w - details_button_.location().w
00791             , image_rect.y + image_rect.h
00792             , details_button_.location().w
00793             , details_button_.location().h);
00794     details_button_.set_location(button_loc);
00795 
00796     SDL_Rect description_rect = create_rect(image_rect.x
00797             , image_rect.y + image_rect.h + details_button_.location().h
00798             , 0
00799             , 0);
00800 
00801     if(det.name.empty() == false) {
00802         std::stringstream desc;
00803         desc << font::BOLD_TEXT << det.name;
00804         const std::string description = desc.str();
00805         description_rect = font::text_area(description, font::SIZE_NORMAL);
00806         description_rect = font::draw_text(&video(), area,
00807                             font::SIZE_NORMAL, font::NORMAL_COLOR,
00808                             desc.str(), right_align ?  image_rect.x :
00809                             image_rect.x + image_rect.w - description_rect.w,
00810                             image_rect.y + image_rect.h + details_button_.location().h);
00811     }
00812 
00813     std::stringstream text;
00814     text << font::unit_type << det.type_name << "\n"
00815         << font::race
00816         << (right_align && !weapons_ ? det.race+"  " : "  "+det.race) << "\n"
00817         << _("level") << " " << det.level << "\n"
00818         << det.alignment << "\n"
00819         << det.traits << "\n";
00820 
00821     for(std::vector<t_string>::const_iterator a = det.abilities.begin(); a != det.abilities.end(); ++a) {
00822         if(a != det.abilities.begin()) {
00823             text << ", ";
00824         }
00825         text << (*a);
00826     }
00827     text << "\n";
00828 
00829     // Use same coloring as in generate_report.cpp:
00830     text << det.hp_color << _("HP: ")
00831         << det.hitpoints << "/" << det.max_hitpoints << "\n";
00832 
00833     text << det.xp_color << _("XP: ")
00834         << det.experience << "/" << det.max_experience << "\n";
00835 
00836     if(weapons_) {
00837         text << _("Moves: ")
00838             << det.movement_left << "/" << det.total_movement << "\n";
00839 
00840         for(std::vector<attack_type>::const_iterator at_it = det.attacks.begin();
00841             at_it != det.attacks.end(); ++at_it) {
00842             // specials_context seems not needed here
00843             //at_it->set_specials_context(map_location(),u);
00844 
00845             // see generate_report() in generate_report.cpp
00846             text << font::weapon
00847                 << at_it->damage()
00848                 << font::weapon_numbers_sep
00849                 << at_it->num_attacks()
00850                 << " " << at_it->name() << "\n";
00851             text << font::weapon_details
00852                 << "  " << string_table["range_" + at_it->range()]
00853                 << font::weapon_details_sep
00854                 << string_table["type_" + at_it->type()] << "\n";
00855 
00856             std::string accuracy_parry = at_it->accuracy_parry_description();
00857             if(accuracy_parry.empty() == false) {
00858                 text << font::weapon_details << "  " << accuracy_parry << "\n";
00859             }
00860 
00861             std::string special = at_it->weapon_specials(true);
00862             if (!special.empty()) {
00863                 text << font::weapon_details << "  " << special << "\n";
00864             }
00865         }
00866     }
00867 
00868     // we don't remove empty lines, so all fields stay at the same place
00869     const std::vector<std::string> lines = utils::split(text.str(), '\n',
00870         utils::STRIP_SPACES & !utils::REMOVE_EMPTY);
00871 
00872 
00873     int ypos = area.y;
00874 
00875     if(weapons_) {
00876         ypos += image_rect.h + description_rect.h + details_button_.location().h;
00877     }
00878 
00879     for(std::vector<std::string>::const_iterator line = lines.begin(); line != lines.end(); ++line) {
00880         int xpos = area.x;
00881         if(right_align && !weapons_) {
00882             const SDL_Rect& line_area = font::text_area(*line,font::SIZE_SMALL);
00883             // right align, but if too long, don't hide line's beginning
00884             if (line_area.w < area.w)
00885                 xpos = area.x + area.w - line_area.w;
00886         }
00887 
00888         SDL_Rect cur_area = font::draw_text(&video(),location(),font::SIZE_SMALL,font::NORMAL_COLOR,*line,xpos,ypos);
00889         ypos += cur_area.h;
00890     }
00891 }
00892 
00893 units_list_preview_pane::units_list_preview_pane(const unit *u, TYPE type, bool on_left_side) :
00894     unit_preview_pane(NULL, type, on_left_side),
00895     units_(1, u)
00896 {
00897 }
00898 
00899 units_list_preview_pane::units_list_preview_pane(const std::vector<const unit *> &units,
00900         const gui::filter_textbox* filter, TYPE type, bool on_left_side) :
00901     unit_preview_pane(filter, type, on_left_side),
00902     units_(units)
00903 {
00904 }
00905 
00906 units_list_preview_pane::units_list_preview_pane(const std::vector<unit> &units,
00907         const gui::filter_textbox* filter, TYPE type, bool on_left_side) :
00908     unit_preview_pane(filter, type, on_left_side),
00909     units_(units.size())
00910 {
00911     for (unsigned i = 0; i < units.size(); ++i)
00912         units_[i] = &units[i];
00913 }
00914 
00915 size_t units_list_preview_pane::size() const
00916 {
00917     return units_.size();
00918 }
00919 
00920 const unit_preview_pane::details units_list_preview_pane::get_details() const
00921 {
00922     const unit &u = *units_[index_];
00923     details det;
00924 
00925     det.image = u.still_image();
00926 
00927     det.name = u.name();
00928     det.type_name = u.type_name();
00929     if(u.race() != NULL) {
00930         det.race = u.race()->name(u.gender());
00931     }
00932     det.level = u.level();
00933     det.alignment = unit_type::alignment_description(u.alignment(), u.gender());
00934     det.traits = utils::join(u.trait_names(), ", ");
00935 
00936     //we filter to remove the tooltips (increment by 2)
00937     const std::vector<boost::tuple<t_string,t_string,t_string> > &abilities = u.ability_tooltips(true);
00938     for(std::vector<boost::tuple<t_string,t_string,t_string> >::const_iterator a = abilities.begin();
00939          a != abilities.end(); ++a) {
00940         det.abilities.push_back(a->get<1>());
00941     }
00942 
00943     det.hitpoints = u.hitpoints();
00944     det.max_hitpoints = u.max_hitpoints();
00945     det.hp_color = font::color2markup(u.hp_color());
00946 
00947     det.experience = u.experience();
00948     det.max_experience = u.max_experience();
00949     det.xp_color = font::color2markup(u.xp_color());
00950 
00951     det.movement_left = u.movement_left();
00952     det.total_movement= u.total_movement();
00953 
00954     det.attacks = u.attacks();
00955     return det;
00956 }
00957 
00958 void units_list_preview_pane::process_event()
00959 {
00960     if (details_button_.pressed() && index_ >= 0 && index_ < int(size())) {
00961         show_unit_description(*units_[index_]);
00962     }
00963 }
00964 
00965 unit_types_preview_pane::unit_types_preview_pane(
00966                     std::vector<const unit_type*>& unit_types, const gui::filter_textbox* filter,
00967                     int side, TYPE type, bool on_left_side)
00968     : unit_preview_pane(filter, type, on_left_side),
00969                       unit_types_(&unit_types), side_(side)
00970 {}
00971 
00972 size_t unit_types_preview_pane::size() const
00973 {
00974     return (unit_types_!=NULL) ? unit_types_->size() : 0;
00975 }
00976 
00977 const unit_types_preview_pane::details unit_types_preview_pane::get_details() const
00978 {
00979     const unit_type* t = (*unit_types_)[index_];
00980     details det;
00981 
00982     if (t==NULL)
00983         return det;
00984 
00985     //FIXME: There should be a better way to deal with this
00986     unit_types.find(t->id(), unit_type::WITHOUT_ANIMATIONS);
00987 
00988     std::string mod = "~RC(" + t->flag_rgb() + ">" + team::get_side_color_index(side_) + ")";
00989     det.image = image::get_image(t->image()+mod);
00990 
00991     det.name = "";
00992     det.type_name = t->type_name();
00993     det.level = t->level();
00994     det.alignment = unit_type::alignment_description(t->alignment(), t->genders().front());
00995 
00996     if (const unit_race *r = unit_types.find_race(t->race())) {
00997         assert(!t->genders().empty());
00998         det.race = r->name(t->genders().front());
00999     }
01000 
01001     //FIXME: This probably must be move into a unit_type function
01002     foreach (const config &tr, t->possible_traits())
01003     {
01004         if (tr["availability"] != "musthave") continue;
01005         std::string gender_string = (!t->genders().empty() && t->genders().front()== unit_race::FEMALE) ? "female_name" : "male_name";
01006         t_string name = tr[gender_string];
01007         if (name.empty()) {
01008             name = tr["name"];
01009         }
01010         if (!name.empty()) {
01011             if (!det.traits.empty()) {
01012                 det.traits += ", ";
01013             }
01014             det.traits += name;
01015         }
01016     }
01017 
01018     det.abilities = t->abilities();
01019 
01020     det.hitpoints = t->hitpoints();
01021     det.max_hitpoints = t->hitpoints();
01022     det.hp_color = "<33,225,0>"; // from unit::hp_color()
01023 
01024     det.experience = 0;
01025     det.max_experience = t->experience_needed();
01026     det.xp_color = "<0,160,225>"; // from unit::xp_color()
01027 
01028     // Check if AMLA color is needed
01029     // FIXME: not sure if it's fully accurate (but not very important for unit_type)
01030     // xp_color also need a simpler function for doing this
01031     foreach (const config &adv, t->modification_advancements())
01032     {
01033         if (!adv["strict_amla"].to_bool() || !t->can_advance()) {
01034             det.xp_color = "<170,0,255>"; // from unit::xp_color()
01035             break;
01036         }
01037     }
01038 
01039     det.movement_left = 0;
01040     det.total_movement= t->movement();
01041 
01042     det.attacks = t->attacks();
01043     return det;
01044 }
01045 
01046 void unit_types_preview_pane::process_event()
01047 {
01048     if (details_button_.pressed() && index_ >= 0 && index_ < int(size())) {
01049         const unit_type* type = (*unit_types_)[index_];
01050         if (type != NULL)
01051             show_unit_description(*type);
01052     }
01053 }
01054 
01055 
01056 void show_unit_description(const unit &u)
01057 {
01058     const unit_type* t = u.type();
01059     if (t != NULL)
01060         show_unit_description(*t);
01061     else
01062         // can't find type, try open the id page to have feedback and unit error page
01063       help::show_unit_help(*resources::screen, u.type_id());
01064 }
01065 
01066 void show_unit_description(const unit_type &t)
01067 {
01068     help::show_unit_help(*resources::screen, t.id(), t.hide_help());
01069 }
01070 
01071 static network::connection network_data_dialog(display& disp, const std::string& msg, config& cfg, network::connection connection_num, network::statistics (*get_stats)(network::connection handle))
01072 {
01073     const size_t width = 300;
01074     const size_t height = 80;
01075     const size_t border = 20;
01076     const int left = disp.w()/2 - width/2;
01077     const int top  = disp.h()/2 - height/2;
01078 
01079     const events::event_context dialog_events_context;
01080 
01081     gui::button cancel_button(disp.video(),_("Cancel"));
01082     std::vector<gui::button*> buttons_ptr(1,&cancel_button);
01083 
01084     gui::dialog_frame frame(disp.video(), msg, gui::dialog_frame::default_style, true, &buttons_ptr);
01085     SDL_Rect centered_layout = frame.layout(left,top,width,height).interior;
01086     centered_layout.x = disp.w() / 2 - centered_layout.w / 2;
01087     centered_layout.y = disp.h() / 2 - centered_layout.h / 2;
01088     // HACK: otherwise we get an empty useless space in the dialog below the progressbar
01089     centered_layout.h = height;
01090     frame.layout(centered_layout);
01091     frame.draw();
01092 
01093     const SDL_Rect progress_rect = create_rect(centered_layout.x + border
01094             , centered_layout.y + border
01095             , centered_layout.w - border * 2
01096             , centered_layout.h - border * 2);
01097 
01098     gui::progress_bar progress(disp.video());
01099     progress.set_location(progress_rect);
01100 
01101     events::raise_draw_event();
01102     disp.flip();
01103 
01104     network::statistics old_stats = get_stats(connection_num);
01105 
01106     cfg.clear();
01107     for(;;) {
01108         const network::connection res = network::receive_data(cfg,connection_num,100);
01109         const network::statistics stats = get_stats(connection_num);
01110         if(stats.current_max != 0 && stats != old_stats) {
01111             old_stats = stats;
01112             progress.set_progress_percent((stats.current*100)/stats.current_max);
01113             std::ostringstream stream;
01114             stream << stats.current/1024 << "/" << stats.current_max/1024 << _("KB");
01115             progress.set_text(stream.str());
01116         }
01117 
01118         events::raise_draw_event();
01119         disp.flip();
01120         events::pump();
01121 
01122         if(res != 0) {
01123             return res;
01124         }
01125 
01126 
01127         if(cancel_button.pressed()) {
01128             return res;
01129         }
01130     }
01131 }
01132 
01133 network::connection network_send_dialog(display& disp, const std::string& msg, config& cfg, network::connection connection_num)
01134 {
01135     return network_data_dialog(disp, msg, cfg, connection_num,
01136                                network::get_send_stats);
01137 }
01138 
01139 network::connection network_receive_dialog(display& disp, const std::string& msg, config& cfg, network::connection connection_num)
01140 {
01141     return network_data_dialog(disp, msg, cfg, connection_num,
01142                                network::get_receive_stats);
01143 }
01144 
01145 } // end namespace dialogs
01146 
01147 namespace {
01148 
01149 class connect_waiter : public threading::waiter
01150 {
01151 public:
01152     connect_waiter(display& disp, gui::button& button) : disp_(disp), button_(button)
01153     {}
01154     ACTION process();
01155 
01156 private:
01157     display& disp_;
01158     gui::button& button_;
01159 };
01160 
01161 connect_waiter::ACTION connect_waiter::process()
01162 {
01163     events::raise_draw_event();
01164     disp_.flip();
01165     events::pump();
01166     if(button_.pressed()) {
01167         return ABORT;
01168     } else {
01169         return WAIT;
01170     }
01171 }
01172 
01173 }
01174 
01175 namespace dialogs
01176 {
01177 
01178 network::connection network_connect_dialog(display& disp, const std::string& msg, const std::string& hostname, int port)
01179 {
01180     const size_t width = 250;
01181     const size_t height = 20;
01182     const int left = disp.w()/2 - width/2;
01183     const int top  = disp.h()/2 - height/2;
01184 
01185     const events::event_context dialog_events_context;
01186 
01187     gui::button cancel_button(disp.video(),_("Cancel"));
01188     std::vector<gui::button*> buttons_ptr(1,&cancel_button);
01189 
01190     gui::dialog_frame frame(disp.video(), msg, gui::dialog_frame::default_style, true, &buttons_ptr);
01191     frame.layout(left,top,width,height);
01192     frame.draw();
01193 
01194     events::raise_draw_event();
01195     disp.flip();
01196 
01197     connect_waiter waiter(disp,cancel_button);
01198     return network::connect(hostname,port,waiter);
01199 }
01200 
01201 } // end namespace dialogs
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines

Generated by doxygen 1.7.1 on Thu May 24 2012 01:02:33 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs