00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018 #include "global.hpp"
00019
00020 #include "dialogs.hpp"
00021 #include "game_errors.hpp"
00022 #include "game_events.hpp"
00023 #include "game_preferences.hpp"
00024 #include "gettext.hpp"
00025 #include "help.hpp"
00026 #include "language.hpp"
00027 #include "log.hpp"
00028 #include "marked-up_text.hpp"
00029 #include "menu_events.hpp"
00030 #include "minimap.hpp"
00031 #include "replay.hpp"
00032 #include "thread.hpp"
00033 #include "unit.hpp"
00034 #include "wml_separators.hpp"
00035 #include "widgets/progressbar.hpp"
00036 #include "wml_exception.hpp"
00037
00038 #include <assert.h>
00039 #include <clocale>
00040
00041 #define LOG_NG LOG_STREAM(info, engine)
00042 #define LOG_DP LOG_STREAM(info, display)
00043 #define ERR_G LOG_STREAM(err, general)
00044 #define ERR_CF LOG_STREAM(err, config)
00045
00046 namespace dialogs
00047 {
00048
00049 void advance_unit(const gamemap& map,
00050 unit_map& units,
00051 gamemap::location loc,
00052 game_display& gui,
00053 bool random_choice,
00054 const bool add_replay_event)
00055 {
00056 unit_map::iterator u = units.find(loc);
00057 if(u == units.end() || u->second.advances() == false)
00058 return;
00059
00060 LOG_DP << "advance_unit: " << u->second.type_id() << "\n";
00061
00062 const std::vector<std::string>& options = u->second.advances_to();
00063
00064 std::vector<std::string> lang_options;
00065
00066 std::vector<unit> sample_units;
00067 for(std::vector<std::string>::const_iterator op = options.begin(); op != options.end(); ++op) {
00068 sample_units.push_back(::get_advanced_unit(units,loc,*op));
00069 const unit& type = sample_units.back();
00070
00071 #ifdef LOW_MEM
00072 lang_options.push_back(IMAGE_PREFIX + type.absolute_image() + COLUMN_SEPARATOR + type.type_name());
00073 #else
00074 lang_options.push_back(IMAGE_PREFIX + type.absolute_image() + u->second.image_mods() + COLUMN_SEPARATOR + type.type_name());
00075 #endif
00076 preferences::encountered_units().insert(*op);
00077 }
00078
00079 const config::child_list& mod_options = u->second.get_modification_advances();
00080
00081 for(config::child_list::const_iterator mod = mod_options.begin(); mod != mod_options.end(); ++mod) {
00082 sample_units.push_back(::get_advanced_unit(units,loc,u->second.type_id()));
00083 sample_units.back().add_modification("advance",**mod);
00084 const unit& type = sample_units.back();
00085 if((**mod)["image"].str().size()){
00086 lang_options.push_back(IMAGE_PREFIX + (**mod)["image"].str() + COLUMN_SEPARATOR + (**mod)["description"].str());
00087 }else{
00088 #ifdef LOW_MEM
00089 lang_options.push_back(IMAGE_PREFIX + type.absolute_image() + COLUMN_SEPARATOR + (**mod)["description"].str());
00090 #else
00091 lang_options.push_back(IMAGE_PREFIX + type.absolute_image() + u->second.image_mods() + COLUMN_SEPARATOR + (**mod)["description"].str());
00092 #endif
00093 }
00094 }
00095
00096 LOG_DP << "options: " << options.size() << "\n";
00097
00098 int res = 0;
00099
00100 if(lang_options.empty()) {
00101 return;
00102 } else if(random_choice) {
00103 res = rand()%lang_options.size();
00104 } else if(lang_options.size() > 1) {
00105
00106 units_list_preview_pane unit_preview(gui,&map,sample_units);
00107 std::vector<gui::preview_pane*> preview_panes;
00108 preview_panes.push_back(&unit_preview);
00109
00110 gui::dialog advances = gui::dialog(gui,
00111 _("Advance Unit"),
00112 _("What should our victorious unit become?"),
00113 gui::OK_ONLY);
00114 advances.set_menu(lang_options);
00115 advances.set_panes(preview_panes);
00116 res = advances.show();
00117 }
00118
00119 if(add_replay_event) {
00120 recorder.add_advancement(loc);
00121 }
00122
00123 recorder.choose_option(res);
00124
00125 LOG_DP << "animating advancement...\n";
00126 animate_unit_advancement(units,loc,gui,size_t(res));
00127
00128
00129
00130
00131 u = units.find(loc);
00132 if(u != units.end()) {
00133
00134 if(u->second.experience() < 81) {
00135
00136
00137 advance_unit(map, units, loc, gui, random_choice, true);
00138 } else {
00139 LOG_STREAM(err, config) << "Unit has an too high amount of " << u->second.experience()
00140 << " XP left, cascade leveling disabled\n";
00141 }
00142 } else {
00143 LOG_STREAM(err, engine) << "Unit advanced no longer exists\n";
00144 }
00145 }
00146
00147 bool animate_unit_advancement(unit_map& units, gamemap::location loc, game_display& gui, size_t choice)
00148 {
00149 const events::command_disabler cmd_disabler;
00150
00151 unit_map::iterator u = units.find(loc);
00152 if(u == units.end() || u->second.advances() == false) {
00153 return false;
00154 }
00155
00156 const std::vector<std::string>& options = u->second.advances_to();
00157 const config::child_list& mod_options = u->second.get_modification_advances();
00158
00159 if(choice >= options.size() + mod_options.size()) {
00160 return false;
00161 }
00162
00163
00164
00165
00166 if(!gui.video().update_locked()) {
00167 unit_animator animator;
00168 animator.add_animation(&u->second,"levelout",u->first);
00169 animator.start_animations();
00170 animator.wait_for_end();
00171 }
00172
00173 if(choice < options.size()) {
00174 const std::string& chosen_unit = options[choice];
00175 ::advance_unit(units,loc,chosen_unit);
00176 } else {
00177 unit amla_unit(u->second);
00178
00179 LOG_NG << "firing advance event (AMLA)\n";
00180 game_events::fire("advance",loc);
00181
00182 amla_unit.get_experience(-amla_unit.max_experience());
00183 amla_unit.add_modification("advance",*mod_options[choice - options.size()]);
00184 units.replace(new std::pair<gamemap::location,unit>(loc,amla_unit));
00185
00186 LOG_NG << "firing post_advance event (AMLA)\n";
00187 game_events::fire("post_advance",loc);
00188 }
00189
00190 u = units.find(loc);
00191 gui.invalidate_unit();
00192
00193 if(u != units.end() && !gui.video().update_locked()) {
00194 unit_animator animator;
00195 animator.add_animation(&u->second,"levelin",u->first);
00196 animator.start_animations();
00197 animator.wait_for_end();
00198 u->second.set_standing(u->first);
00199 gui.invalidate(loc);
00200 gui.draw();
00201 events::pump();
00202 }
00203
00204 gui.invalidate_all();
00205 gui.draw();
00206
00207 return true;
00208 }
00209
00210 void show_objectives(game_display& disp, const config& level, const std::string& objectives)
00211 {
00212 static const std::string no_objectives(_("No objectives available"));
00213 const std::string& name = level["name"];
00214 std::string campaign_name = std::string(level["campaign"]);
00215 replace_underbar2space(campaign_name);
00216
00217 gui::message_dialog(disp, "", "*~" + name +
00218 (campaign_name.empty() ? "\n" : " - " + campaign_name + "\n") +
00219 (objectives.empty() ? no_objectives : objectives)
00220 ).show();
00221 }
00222
00223 bool is_illegal_file_char(char c)
00224 {
00225 return c == '/' || c == '\\' || c == ':'
00226 #ifdef WIN32
00227 || c == '?' || c == '|' || c == '<' || c == '>' || c == '*' || c == '"'
00228 #endif
00229 ;
00230 }
00231
00232 int get_save_name(display & disp,const std::string& message, const std::string& txt_label,
00233 std::string* fname, gui::DIALOG_TYPE dialog_type, const std::string& title,
00234 const bool has_exit_button, const bool ask_for_filename)
00235 {
00236 static int quit_prompt = 0;
00237 const std::string& tmp_title = (title.empty()) ? _("Save Game") : title;
00238 bool ignore_opt = false;
00239 int overwrite=0;
00240 int res=0;
00241 bool ask = ask_for_filename;
00242 do {
00243 if (ask) {
00244 gui::dialog d(disp, tmp_title, message, dialog_type);
00245 d.set_textbox(txt_label, *fname);
00246 if(has_exit_button) {
00247 d.add_button(new gui::dialog_button(disp.video(), _("Quit Game"),
00248 gui::button::TYPE_PRESS, 2), gui::dialog::BUTTON_STANDARD);
00249 if(quit_prompt < 0) {
00250 res = 1;
00251 } else if(quit_prompt > 5) {
00252 d.add_button(new gui::dialog_button(disp.video(), _("Ignore All"),
00253 gui::button::TYPE_CHECK), gui::dialog::BUTTON_CHECKBOX);
00254 res = d.show();
00255 ignore_opt = d.option_checked();
00256 } else {
00257 res = d.show();
00258 if(res == 1) {
00259 ++quit_prompt;
00260 } else {
00261 quit_prompt = 0;
00262 }
00263 }
00264 } else {
00265 res = d.show();
00266 }
00267 *fname = d.textbox_text();
00268 } else {
00269 ask = true;
00270 }
00271
00272 if (std::count_if(fname->begin(),fname->end(),is_illegal_file_char)) {
00273 gui::message_dialog(disp, _("Error"),
00274 _("Save names may not contain colons, slashes, or backslashes. "
00275 "Please choose a different name.")).show();
00276 overwrite = 1;
00277 continue;
00278 }
00279
00280 if (is_gzip_file(*fname)) {
00281 gui::message_dialog(disp, _("Error"),
00282 _("Save names should not end on '.gz'. "
00283 "Please choose a different name.")).show();
00284 overwrite = 1;
00285 continue;
00286 }
00287
00288 if (res == 0 && save_game_exists(*fname)) {
00289 std::stringstream s;
00290 s << _("Save already exists. Do you want to overwrite it?")
00291 << std::endl << _("Name: ") << *fname;
00292 overwrite = gui::dialog(disp,_("Overwrite?"),
00293 s.str(), gui::YES_NO).show();
00294 } else {
00295 overwrite = 0;
00296 }
00297 } while ((res == 0) && (overwrite != 0));
00298
00299 if(ignore_opt) {
00300 quit_prompt = -1;
00301 }
00302 return res;
00303 }
00304
00305
00306 namespace {
00307
00308 class load_game_filter_textbox : public gui::dialog_textbox {
00309 public:
00310 load_game_filter_textbox(CVideo& video, const std::vector<std::string>& items, gui::dialog& dialog)
00311 : gui::dialog_textbox(new gui::label(video, _("Filter: ")), video, 250),
00312 items_(items),
00313 dialog_(dialog),
00314 first_time_(true)
00315 {
00316 sorter_.set_alpha_sort(0).set_id_sort(1);
00317 set_text("");
00318 }
00319
00320 int get_save_index(int index) const {
00321
00322
00323
00324 ++index;
00325 if(size_t(index) >= index_map_.size()) {
00326 return -1;
00327 }
00328 return index_map_[index]-1;
00329 }
00330 private:
00331 std::vector<std::string> items_, filtered_items_;
00332 std::vector<int> index_map_;
00333 gui::dialog& dialog_;
00334 gui::menu::basic_sorter sorter_;
00335 bool first_time_;
00336 virtual void handle_text_changed(const wide_string& text) {
00337 filtered_items_.clear();
00338 index_map_.clear();
00339 const std::string t = utils::wstring_to_string(text);
00340 for(size_t n = 0; n != items_.size(); ++n) {
00341 if(n == 0 || std::search(items_[n].begin(), items_[n].end(),
00342 t.begin(), t.end(), chars_equal_insensitive) != items_[n].end()) {
00343 filtered_items_.push_back(items_[n]);
00344 index_map_.push_back(n);
00345 }
00346 }
00347
00348 if(first_time_) {
00349 dialog_.set_menu(filtered_items_, &sorter_);
00350 first_time_ = false;
00351 } else {
00352 dialog_.set_menu_items(filtered_items_);
00353 }
00354 }
00355 };
00356
00357 class delete_save : public gui::dialog_button_action
00358 {
00359 public:
00360 delete_save(display& disp, load_game_filter_textbox& filter, std::vector<save_info>& saves, std::vector<config*>& save_summaries) : disp_(disp), saves_(saves), summaries_(save_summaries), filter_(filter) {}
00361 private:
00362 gui::dialog_button_action::RESULT button_pressed(int menu_selection);
00363
00364 display& disp_;
00365 std::vector<save_info>& saves_;
00366 std::vector<config*>& summaries_;
00367 load_game_filter_textbox& filter_;
00368 };
00369
00370 gui::dialog_button_action::RESULT delete_save::button_pressed(int menu_selection)
00371 {
00372 const size_t index = size_t(filter_.get_save_index(menu_selection));
00373 if(index < saves_.size()) {
00374
00375
00376 if(preferences::ask_delete_saves()) {
00377 gui::dialog dmenu(disp_,"",
00378 _("Do you really want to delete this game?"),
00379 gui::YES_NO);
00380 dmenu.add_option(_("Don't ask me again!"), true);
00381 const int res = dmenu.show();
00382
00383 if(dmenu.option_checked()) {
00384 preferences::set_ask_delete_saves(false);
00385 }
00386
00387 if(res != 0) {
00388 return gui::CONTINUE_DIALOG;
00389 }
00390 }
00391
00392
00393 delete_game(saves_[index].name);
00394
00395
00396 saves_.erase(saves_.begin() + index);
00397
00398 if(index < summaries_.size()) {
00399 summaries_.erase(summaries_.begin() + index);
00400 }
00401
00402 return gui::DELETE_ITEM;
00403 } else {
00404 return gui::CONTINUE_DIALOG;
00405 }
00406 }
00407
00408 static const int save_preview_border = 10;
00409
00410 class save_preview_pane : public gui::preview_pane
00411 {
00412 public:
00413 save_preview_pane(CVideo &video, const config& game_config, gamemap* map,
00414 const std::vector<save_info>& info, const std::vector<config*>& summaries, const load_game_filter_textbox& textbox)
00415 : gui::preview_pane(video), game_config_(&game_config), map_(map), info_(&info), summaries_(&summaries), index_(0), textbox_(textbox)
00416 {
00417 set_measurements(minimum<int>(200,video.getx()/4),
00418 minimum<int>(400,video.gety() * 4/5));
00419 }
00420
00421 void draw_contents();
00422 void set_selection(int index) {
00423 index_ = textbox_.get_save_index(index);
00424 set_dirty();
00425 }
00426
00427 bool left_side() const { return true; }
00428
00429 private:
00430 const config* game_config_;
00431 gamemap* map_;
00432 const std::vector<save_info>* info_;
00433 const std::vector<config*>* summaries_;
00434 int index_;
00435 std::map<std::string,surface> map_cache_;
00436 const load_game_filter_textbox& textbox_;
00437 };
00438
00439 void save_preview_pane::draw_contents()
00440 {
00441 if (size_t(index_) >= summaries_->size() || info_->size() != summaries_->size()) {
00442 return;
00443 }
00444
00445 std::string dummy;
00446 config& summary = *(*summaries_)[index_];
00447 if (summary["label"] == ""){
00448 try {
00449 load_game_summary((*info_)[index_].name, summary, &dummy);
00450 *(*summaries_)[index_] = summary;
00451 } catch(game::load_game_failed&) {
00452 summary["corrupt"] = "yes";
00453 }
00454 }
00455
00456 surface const screen = video().getSurface();
00457
00458 SDL_Rect const &loc = location();
00459 const SDL_Rect area = { loc.x + save_preview_border, loc.y + save_preview_border,
00460 loc.w - save_preview_border * 2, loc.h - save_preview_border * 2 };
00461 SDL_Rect clip_area = area;
00462 const clip_rect_setter clipper(screen,clip_area);
00463
00464 int ypos = area.y;
00465
00466 const unit_type_data::unit_type_map::const_iterator leader = unit_type_data::types().find(summary["leader"]);
00467 if(leader != unit_type_data::types().end()) {
00468
00469 #ifdef LOW_MEM
00470 const surface image(image::get_image(leader->second.image()));
00471 #else
00472 const surface image(image::get_image(leader->second.image() + "~RC(" + leader->second.flag_rgb() + ">1)"));
00473 #endif
00474
00475 if(image != NULL) {
00476 SDL_Rect image_rect = {area.x,area.y,image->w,image->h};
00477 ypos += image_rect.h + save_preview_border;
00478
00479 SDL_BlitSurface(image,NULL,screen,&image_rect);
00480 }
00481 }
00482
00483 std::string map_data = summary["map_data"];
00484 if(map_data.empty()) {
00485 const config* const scenario = game_config_->find_child(summary["campaign_type"],"id",summary["scenario"]);
00486 if(scenario != NULL && scenario->find_child("side","shroud","yes") == NULL) {
00487 map_data = (*scenario)["map_data"];
00488 if(map_data.empty() && (*scenario)["map"].empty() == false) {
00489 try {
00490 map_data = read_map((*scenario)["map"]);
00491 } catch(io_exception& e) {
00492 ERR_G << "could not read map '" << (*scenario)["map"] << "': " << e.what() << "\n";
00493 }
00494 }
00495 }
00496 }
00497
00498 surface map_surf(NULL);
00499
00500 if(map_data.empty() == false) {
00501 const std::map<std::string,surface>::const_iterator itor = map_cache_.find(map_data);
00502 if(itor != map_cache_.end()) {
00503 map_surf = itor->second;
00504 } else if(map_ != NULL) {
00505 try {
00506 #ifdef USE_TINY_GUI
00507 const int minimap_size = 60;
00508 #else
00509 const int minimap_size = 100;
00510 #endif
00511 map_->read(map_data);
00512
00513 map_surf = image::getMinimap(minimap_size, minimap_size, *map_);
00514 if(map_surf != NULL) {
00515 map_cache_.insert(std::pair<std::string,surface>(map_data,surface(map_surf)));
00516 }
00517 } catch(gamemap::incorrect_format_exception& e) {
00518 ERR_CF << "map could not be loaded: " << e.msg_ << '\n';
00519 } catch(twml_exception& e) {
00520 ERR_CF << "map could not be loaded: " << e.dev_message << '\n';
00521 }
00522 }
00523 }
00524
00525 if(map_surf != NULL) {
00526 SDL_Rect map_rect = {area.x + area.w - map_surf->w,area.y,map_surf->w,map_surf->h};
00527 ypos = maximum<int>(ypos,map_rect.y + map_rect.h + save_preview_border);
00528 SDL_BlitSurface(map_surf,NULL,screen,&map_rect);
00529 }
00530
00531 char* old_locale = std::setlocale(LC_TIME, get_locale().localename.c_str());
00532 char time_buf[256] = {0};
00533 const save_info& save = (*info_)[index_];
00534 tm* tm_l = localtime(&save.time_modified);
00535 if (tm_l) {
00536 const size_t res = strftime(time_buf,sizeof(time_buf),_("%a %b %d %H:%M %Y"),tm_l);
00537 if(res == 0) {
00538 time_buf[0] = 0;
00539 }
00540 } else {
00541 LOG_NG << "localtime() returned null for time " << save.time_modified << ", save " << save.name;
00542 }
00543
00544 if(old_locale) {
00545 std::setlocale(LC_TIME, old_locale);
00546 }
00547
00548 std::stringstream str;
00549
00550
00551 std::string name = (*info_)[index_].name;
00552 str << font::BOLD_TEXT << utils::escape(name) << "\n" << time_buf;
00553
00554 const std::string& campaign_type = summary["campaign_type"];
00555 if(utils::string_bool(summary["corrupt"], false)) {
00556 str << "\n" << _("#(Invalid)");
00557 } else if (!campaign_type.empty()) {
00558 str << "\n";
00559
00560 if(campaign_type == "scenario") {
00561 const std::string campaign_id = summary["campaign"];
00562 const config* campaign = campaign_id.empty() ? NULL : game_config_->find_child("campaign", "id", campaign_id);
00563 utils::string_map symbols;
00564 if (campaign != NULL) {
00565 symbols["campaign_name"] = (*campaign)["name"];
00566 } else {
00567
00568 symbols["campaign_name"] = "(" + campaign_id + ")";
00569 }
00570 str << vgettext("Campaign: $campaign_name", symbols);
00571
00572
00573 if (game_config::debug && (campaign != NULL)) {
00574 str << '\n' << "(" << campaign_id << ")";
00575 }
00576 } else if(campaign_type == "multiplayer") {
00577 str << _("Multiplayer");
00578 } else if(campaign_type == "tutorial") {
00579 str << _("Tutorial");
00580 } else {
00581 str << campaign_type;
00582 }
00583
00584 str << "\n";
00585
00586 if(utils::string_bool(summary["replay"], false) && !utils::string_bool(summary["snapshot"], true)) {
00587 str << _("replay");
00588 } else if (!summary["turn"].empty()) {
00589 str << _("Turn") << " " << summary["turn"];
00590 } else {
00591 str << _("Scenario Start");
00592 }
00593
00594 str << "\n" << _("Difficulty: ") << string_table[summary["difficulty"]];
00595 if(!summary["version"].empty()) {
00596 str << "\n" << _("Version: ") << summary["version"];
00597 }
00598 }
00599
00600 font::draw_text(&video(), area, font::SIZE_SMALL, font::NORMAL_COLOUR, str.str(), area.x, ypos, true);
00601 }
00602
00603 std::string format_time_summary(time_t t)
00604 {
00605 time_t curtime = time(NULL);
00606 const struct tm* timeptr = localtime(&curtime);
00607 if(timeptr == NULL) {
00608 return "";
00609 }
00610
00611 const struct tm current_time = *timeptr;
00612
00613 timeptr = localtime(&t);
00614 if(timeptr == NULL) {
00615 return "";
00616 }
00617
00618 const struct tm save_time = *timeptr;
00619
00620 const char* format_string = _("%b %d %y");
00621
00622 if(current_time.tm_year == save_time.tm_year) {
00623 const int days_apart = current_time.tm_yday - save_time.tm_yday;
00624 if(days_apart == 0) {
00625
00626 format_string = _("%H:%M");
00627 } else if(days_apart > 0 && days_apart <= current_time.tm_wday) {
00628
00629 format_string = _("%A, %H:%M");
00630 } else {
00631
00632 format_string = _("%b %d");
00633 }
00634 } else {
00635
00636 format_string = _("%b %d %y");
00637 }
00638
00639 char buf[40];
00640 const size_t res = strftime(buf,sizeof(buf),format_string,&save_time);
00641 if(res == 0) {
00642 buf[0] = 0;
00643 }
00644
00645 return buf;
00646 }
00647
00648 }
00649
00650 std::string load_game_dialog(display& disp, const config& game_config, bool* show_replay, bool* cancel_orders)
00651 {
00652 std::vector<save_info> games;
00653 {
00654 cursor::setter cur(cursor::WAIT);
00655 games = get_saves_list();
00656 }
00657
00658 if(games.empty()) {
00659 gui::message_dialog(disp,
00660 _("No Saved Games"),
00661 _("There are no saved games to load.\n\n(Games are saved automatically when you complete a scenario)")).show();
00662 return "";
00663 }
00664
00665 std::vector<config*> summaries;
00666 std::vector<save_info>::const_iterator i;
00667 for(i = games.begin(); i != games.end(); ++i) {
00668 config& cfg = save_summary(i->name);
00669 summaries.push_back(&cfg);
00670 }
00671
00672 const events::event_context context;
00673
00674 std::vector<std::string> items;
00675 std::ostringstream heading;
00676 heading << HEADING_PREFIX << _("Name") << COLUMN_SEPARATOR << _("Date");
00677 items.push_back(heading.str());
00678
00679 for(i = games.begin(); i != games.end(); ++i) {
00680 std::string name = i->name;
00681 utils::truncate_as_wstring(name, minimum<size_t>(name.size(), 40));
00682
00683 std::ostringstream str;
00684 str << name << COLUMN_SEPARATOR << format_time_summary(i->time_modified);
00685
00686 items.push_back(str.str());
00687 }
00688
00689 gui::menu::basic_sorter sorter;
00690 sorter.set_alpha_sort(0).set_id_sort(1);
00691
00692 gamemap map_obj(game_config, "");
00693
00694
00695 gui::dialog lmenu(disp,
00696 _("Load Game"),
00697 _("Choose the game to load"), gui::NULL_DIALOG);
00698 lmenu.set_basic_behavior(gui::OK_CANCEL);
00699 load_game_filter_textbox* filter = new load_game_filter_textbox(disp.video(), items, lmenu);
00700 save_preview_pane save_preview(disp.video(),game_config,&map_obj,games,summaries,*filter);
00701 lmenu.set_textbox(filter);
00702 lmenu.add_pane(&save_preview);
00703
00704 if(show_replay != NULL) {
00705 #ifdef USE_SMALL_GUI
00706 lmenu.add_option(_("Show replay"), false, gui::dialog::BUTTON_STANDARD);
00707 #else
00708 lmenu.add_option(_("Show replay"), false);
00709 #endif
00710 }
00711 if(cancel_orders != NULL) {
00712 #ifdef USE_SMALL_GUI
00713 lmenu.add_option(_("Cancel orders"), false, gui::dialog::BUTTON_STANDARD);
00714 #else
00715 lmenu.add_option(_("Cancel orders"), false);
00716 #endif
00717 }
00718 lmenu.add_button(new gui::standard_dialog_button(disp.video(),_("OK"),0,false), gui::dialog::BUTTON_STANDARD);
00719 lmenu.add_button(new gui::standard_dialog_button(disp.video(),_("Cancel"),1,true), gui::dialog::BUTTON_STANDARD);
00720
00721 delete_save save_deleter(disp,*filter,games,summaries);
00722 gui::dialog_button_info delete_button(&save_deleter,_("Delete Save"));
00723 #ifdef USE_SMALL_GUI
00724
00725 lmenu.add_button(delete_button,gui::dialog::BUTTON_HELP);
00726 #else
00727 lmenu.add_button(delete_button);
00728 #endif
00729
00730 int res = lmenu.show();
00731
00732 write_save_index();
00733
00734 if(res == -1)
00735 return "";
00736
00737 res = filter->get_save_index(res);
00738 int option_index = 0;
00739 if(show_replay != NULL) {
00740 *show_replay = lmenu.option_checked(option_index++);
00741
00742 const config& summary = *summaries[res];
00743 if(utils::string_bool(summary["replay"], false) && !utils::string_bool(summary["snapshot"], true)) {
00744 *show_replay = true;
00745 }
00746 }
00747 if (cancel_orders != NULL) {
00748 *cancel_orders = lmenu.option_checked(option_index++);
00749 }
00750
00751 return games[res].name;
00752 }
00753
00754 namespace {
00755 static const int unit_preview_border = 10;
00756 }
00757
00758
00759
00760 unit_preview_pane::unit_preview_pane(game_display& disp, const gamemap* map, TYPE type, bool on_left_side)
00761 : gui::preview_pane(disp.video()), disp_(disp), map_(map), index_(0),
00762 details_button_(disp.video(),_("Profile"),gui::button::TYPE_PRESS,"lite_small",gui::button::MINIMUM_SPACE),
00763 left_(on_left_side), weapons_(type == SHOW_ALL)
00764 {
00765 unsigned w = font::relative_size(weapons_ ? 200 : 190);
00766 unsigned h = font::relative_size(weapons_ ? 370 : 140);
00767 set_measurements(w, h);
00768 }
00769
00770
00771 handler_vector unit_preview_pane::handler_members()
00772 {
00773 handler_vector h;
00774 h.push_back(&details_button_);
00775 return h;
00776 }
00777
00778 bool unit_preview_pane::show_above() const
00779 {
00780 return !weapons_;
00781 }
00782
00783 bool unit_preview_pane::left_side() const
00784 {
00785 return left_;
00786 }
00787
00788 void unit_preview_pane::set_selection(int index)
00789 {
00790 index = minimum<int>(int(size()-1),index);
00791 if(index != index_ && index >= 0) {
00792 index_ = index;
00793 set_dirty();
00794 if(map_ != NULL) {
00795 details_button_.set_dirty();
00796 }
00797 }
00798 }
00799
00800 void unit_preview_pane::draw_contents()
00801 {
00802 if(index_ < 0 || index_ >= int(size())) {
00803 return;
00804 }
00805
00806 const details det = get_details();
00807
00808 const bool right_align = left_side();
00809
00810 surface const screen = video().getSurface();
00811
00812 SDL_Rect const &loc = location();
00813 const SDL_Rect area = { loc.x + unit_preview_border, loc.y + unit_preview_border,
00814 loc.w - unit_preview_border * 2, loc.h - unit_preview_border * 2 };
00815 SDL_Rect clip_area = area;
00816 const clip_rect_setter clipper(screen,clip_area);
00817
00818 surface unit_image = det.image;
00819 if (!left_)
00820 unit_image = image::reverse_image(unit_image);
00821
00822 SDL_Rect image_rect = {area.x,area.y,0,0};
00823
00824 if(unit_image != NULL) {
00825 SDL_Rect rect = {right_align ? area.x : area.x + area.w - unit_image->w,area.y,unit_image->w,unit_image->h};
00826 SDL_BlitSurface(unit_image,NULL,screen,&rect);
00827 image_rect = rect;
00828 }
00829
00830
00831 if(map_ != NULL) {
00832 const SDL_Rect button_loc = {right_align ? area.x : area.x + area.w - details_button_.location().w,
00833 image_rect.y + image_rect.h,
00834 details_button_.location().w,details_button_.location().h};
00835 details_button_.set_location(button_loc);
00836 }
00837
00838 SDL_Rect description_rect = {image_rect.x,image_rect.y+image_rect.h+details_button_.location().h,0,0};
00839
00840 if(det.name.empty() == false) {
00841 std::stringstream desc;
00842 desc << font::NORMAL_TEXT << det.name;
00843 const std::string description = desc.str();
00844 description_rect = font::text_area(description, font::SIZE_NORMAL);
00845 description_rect = font::draw_text(&video(), area,
00846 font::SIZE_NORMAL, font::NORMAL_COLOUR,
00847 desc.str(), right_align ? image_rect.x :
00848 image_rect.x + image_rect.w - description_rect.w,
00849 image_rect.y + image_rect.h + details_button_.location().h);
00850 }
00851
00852 std::stringstream text;
00853 text << det.type_name << "\n"
00854 << font::BOLD_TEXT << _("level") << " " << det.level << "\n"
00855 << det.alignment << "\n"
00856 << det.traits << "\n";
00857
00858 for(std::vector<std::string>::const_iterator a = det.abilities.begin(); a != det.abilities.end(); a++) {
00859 if(a != det.abilities.begin()) {
00860 text << ", ";
00861 }
00862 text << gettext(a->c_str());
00863 }
00864 text << "\n";
00865
00866
00867 text << det.hp_color << _("HP: ")
00868 << det.hitpoints << "/" << det.max_hitpoints << "\n";
00869
00870 text << det.xp_color << _("XP: ")
00871 << det.experience << "/" << det.max_experience << "\n";
00872
00873 if(weapons_) {
00874 text << _("Moves: ")
00875 << det.movement_left << "/" << det.total_movement << "\n";
00876
00877 for(std::vector<attack_type>::const_iterator at_it = det.attacks.begin();
00878 at_it != det.attacks.end(); ++at_it) {
00879
00880
00881
00882
00883 text << "<245,230,193>" << at_it->name()
00884 << " (" << gettext(at_it->type().c_str()) << ")\n";
00885
00886 std::string special = at_it->weapon_specials(true);
00887 if (!special.empty()) {
00888 text << "<166,146,117> " << special << "\n";
00889 }
00890 std::string accuracy = at_it->accuracy_parry_description();
00891 if(accuracy.empty() == false) {
00892 accuracy += " ";
00893 }
00894
00895 text << "<166,146,117> " << at_it->damage() << "-" << at_it->num_attacks()
00896 << " " << accuracy << "-- " << _(at_it->range().c_str()) << "\n";
00897 }
00898 }
00899
00900
00901 const std::vector<std::string> lines = utils::split(text.str(), '\n',
00902 utils::STRIP_SPACES & !utils::REMOVE_EMPTY);
00903
00904 SDL_Rect cur_area = area;
00905
00906 if(weapons_) {
00907 cur_area.y += image_rect.h + description_rect.h + details_button_.location().h;
00908 }
00909
00910 for(std::vector<std::string>::const_iterator line = lines.begin(); line != lines.end(); ++line) {
00911 int xpos = cur_area.x;
00912 if(right_align && !weapons_) {
00913 const SDL_Rect& line_area = font::text_area(*line,font::SIZE_SMALL);
00914 xpos = cur_area.x + cur_area.w - line_area.w;
00915 }
00916
00917 cur_area = font::draw_text(&video(),location(),font::SIZE_SMALL,font::NORMAL_COLOUR,*line,xpos,cur_area.y);
00918 cur_area.y += cur_area.h;
00919 }
00920 }
00921
00922 units_list_preview_pane::units_list_preview_pane(game_display& disp, const gamemap* map, const unit& u, TYPE type, bool on_left_side)
00923 : unit_preview_pane(disp, map, type, on_left_side),
00924 units_(&unit_store_)
00925 {
00926 unit_store_.push_back(u);
00927 }
00928
00929 units_list_preview_pane::units_list_preview_pane(game_display& disp, const gamemap* map, std::vector<unit>& units, TYPE type, bool on_left_side)
00930 : unit_preview_pane(disp, map, type, on_left_side),
00931 units_(&units)
00932 {}
00933
00934 size_t units_list_preview_pane::size() const
00935 {
00936 return (units_!=NULL) ? units_->size() : 0;
00937 }
00938
00939
00940 const unit_preview_pane::details units_list_preview_pane::get_details() const
00941 {
00942 unit& u = (*units_)[index_];
00943 details det;
00944
00945 det.image = u.still_image();
00946
00947 det.name = u.name();
00948 det.type_name = u.type_name();
00949 det.level = u.level();
00950 det.alignment = unit_type::alignment_description(u.alignment());
00951 det.traits = u.traits_description();
00952
00953
00954 const std::vector<std::string>& abilities = u.unit_ability_tooltips();
00955 for(std::vector<std::string>::const_iterator a = abilities.begin();
00956 a != abilities.end(); a+=2) {
00957 det.abilities.push_back(*a);
00958 }
00959
00960 det.hitpoints = u.hitpoints();
00961 det.max_hitpoints = u.max_hitpoints();
00962 det.hp_color = font::color2markup(u.hp_color());
00963
00964 det.experience = u.experience();
00965 det.max_experience = u.max_experience();
00966 det.xp_color = font::color2markup(u.xp_color());
00967
00968 det.movement_left = u.movement_left();
00969 det.total_movement= u.total_movement();
00970
00971 det.attacks = u.attacks();
00972 return det;
00973 }
00974
00975 void units_list_preview_pane::process_event()
00976 {
00977 if(map_ != NULL && details_button_.pressed() && index_ >= 0 && index_ < int(size())) {
00978 show_unit_description(disp_, (*units_)[index_]);
00979 }
00980 }
00981
00982 unit_types_preview_pane::unit_types_preview_pane(game_display& disp, const gamemap* map, std::vector<const unit_type*>& unit_types, int side, TYPE type, bool on_left_side)
00983 : unit_preview_pane(disp, map, type, on_left_side),
00984 unit_types_(&unit_types), side_(side)
00985 {}
00986
00987 size_t unit_types_preview_pane::size() const
00988 {
00989 return (unit_types_!=NULL) ? unit_types_->size() : 0;
00990 }
00991
00992 const unit_types_preview_pane::details unit_types_preview_pane::get_details() const
00993 {
00994 const unit_type* t = (*unit_types_)[index_];
00995 details det;
00996
00997 if (t==NULL)
00998 return det;
00999
01000
01001 unit_type_data::types().find(t->id(), unit_type::WITHOUT_ANIMATIONS);
01002
01003 std::string mod = "~RC(" + t->flag_rgb() + ">" + team::get_side_colour_index(side_) + ")";
01004 det.image = image::get_image(t->image()+mod);
01005
01006 det.name = "";
01007 det.type_name = t->type_name();
01008 det.level = t->level();
01009 det.alignment = unit_type::alignment_description(t->alignment());
01010
01011
01012 const std::vector<config*> traits = t->possible_traits();
01013 for(std::vector<config*>::const_iterator i = traits.begin(); i != traits.end(); i++) {
01014 if((**(i))["availability"] == "musthave") {
01015 std::string gender_string = (!t->genders().empty() && t->genders().front()== unit_race::FEMALE) ? "female_name" : "male_name";
01016 t_string name = (**i)[gender_string];
01017 if (name.empty()) {
01018 name = (**i)["name"];
01019 }
01020 if (!name.empty()) {
01021 if (i != traits.begin()) {
01022 det.traits += ", ";
01023 }
01024 det.traits += name;
01025 }
01026 }
01027 }
01028
01029 det.abilities = t->abilities();
01030
01031 det.hitpoints = t->hitpoints();
01032 det.max_hitpoints = t->hitpoints();
01033 det.hp_color = "<33,225,0>";
01034
01035 det.experience = 0;
01036 det.max_experience = t->experience_needed();
01037 det.xp_color = "<0,160,225>";
01038
01039
01040
01041
01042 const config::child_list& advances = t->modification_advancements();
01043 for(config::child_list::const_iterator j = advances.begin(); j != advances.end(); ++j) {
01044 if (!utils::string_bool((**j)["strict_amla"]) || !t->can_advance()) {
01045 det.xp_color = "<100,0,150>";
01046 break;
01047 }
01048 }
01049
01050 det.movement_left = 0;
01051 det.total_movement= t->movement();
01052
01053 det.attacks = t->attacks();
01054 return det;
01055 }
01056
01057 void unit_types_preview_pane::process_event()
01058 {
01059 if(map_ != NULL && details_button_.pressed() && index_ >= 0 && index_ < int(size())) {
01060 const unit_type* type = (*unit_types_)[index_];
01061 if (type != NULL)
01062 show_unit_description(disp_, *type);
01063 }
01064 }
01065
01066
01067 void show_unit_description(game_display &disp, const unit& u)
01068 {
01069 const unit_type* t = u.type();
01070 if (t != NULL)
01071 show_unit_description(disp, *t);
01072 else
01073
01074 help::show_unit_help(disp, u.type_id());
01075 }
01076
01077 void show_unit_description(game_display &disp, const unit_type& t)
01078 {
01079 help::show_unit_help(disp, t.id(), t.hide_help());
01080 }
01081
01082
01083 namespace {
01084 static const int campaign_preview_border = font::relative_size(10);
01085 }
01086
01087 campaign_preview_pane::campaign_preview_pane(CVideo &video,std::vector<std::pair<std::string,std::string> >* desc) : gui::preview_pane(video),descriptions_(desc),index_(0)
01088 {
01089
01090 #ifdef USE_TINY_GUI
01091 set_measurements(160, 200);
01092 #else
01093 set_measurements(430, 440);
01094 #endif
01095 }
01096
01097 bool campaign_preview_pane::show_above() const { return false; }
01098 bool campaign_preview_pane::left_side() const { return false; }
01099
01100 void campaign_preview_pane::set_selection(int index)
01101 {
01102 index = minimum<int>(descriptions_->size()-1,index);
01103 if(index != index_ && index >= 0) {
01104 index_ = index;
01105 set_dirty();
01106 }
01107 }
01108
01109 void campaign_preview_pane::draw_contents()
01110 {
01111 if (size_t(index_) >= descriptions_->size()) {
01112 return;
01113 }
01114
01115 const SDL_Rect area = {
01116 location().x+campaign_preview_border,
01117 location().y,
01118 location().w-campaign_preview_border*2,
01119 location().h };
01120
01121
01122 gui::dialog_frame f(video(), "", gui::dialog_frame::preview_style, false);
01123 f.layout(area);
01124 f.draw_background();
01125 f.draw_border();
01126
01127
01128 std::string desc_text;
01129 try {
01130 desc_text = font::word_wrap_text((*descriptions_)[index_].first,
01131 font::SIZE_SMALL, area.w - 2 * campaign_preview_border);
01132 } catch (utils::invalid_utf8_exception&) {
01133 LOG_STREAM(err, engine) << "Invalid utf-8 found, campaign description is ignored.\n";
01134 }
01135 const std::vector<std::string> lines = utils::split(desc_text, '\n',utils::STRIP_SPACES);
01136 SDL_Rect txt_area = { area.x+campaign_preview_border,area.y+campaign_preview_border,0,0 };
01137
01138 for(std::vector<std::string>::const_iterator line = lines.begin(); line != lines.end(); ++line) {
01139 txt_area = font::draw_text(&video(),location(),font::SIZE_SMALL,font::NORMAL_COLOUR,*line,txt_area.x,txt_area.y);
01140 txt_area.y += txt_area.h;
01141 }
01142
01143
01144 surface img(NULL);
01145 const std::string desc_img_name = (*descriptions_)[index_].second;
01146 if(!desc_img_name.empty()) {
01147 img.assign(image::get_image(desc_img_name));
01148 }
01149 if (!img.null()) {
01150 SDL_Rect src_rect,dst_rect;
01151 int max_height = area.h-(txt_area.h+txt_area.y-area.y);
01152
01153 src_rect.x = src_rect.y = 0;
01154 src_rect.w = minimum<int>(area.w,img->w);
01155 src_rect.h = minimum<int>(max_height,img->h);
01156 dst_rect.x = area.x+(area.w-src_rect.w)/2;
01157 dst_rect.y = txt_area.y+((max_height-src_rect.h)*8)/13;
01158 if(dst_rect.y - txt_area.h - txt_area.y >= 120) {
01159
01160 dst_rect.y = txt_area.y + font::get_max_height(font::SIZE_SMALL)*5;
01161 }
01162
01163 SDL_BlitSurface(img,&src_rect,video().getSurface(),&dst_rect);
01164
01165 }
01166 }
01167
01168 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))
01169 {
01170 #ifdef USE_TINY_GUI
01171 const size_t width = 200;
01172 const size_t height = 40;
01173 const size_t border = 10;
01174 #else
01175 const size_t width = 300;
01176 const size_t height = 80;
01177 const size_t border = 20;
01178 #endif
01179 const int left = disp.w()/2 - width/2;
01180 const int top = disp.h()/2 - height/2;
01181
01182 const events::event_context dialog_events_context;
01183
01184 gui::button cancel_button(disp.video(),_("Cancel"));
01185 std::vector<gui::button*> buttons_ptr(1,&cancel_button);
01186
01187 gui::dialog_frame frame(disp.video(), msg, gui::dialog_frame::default_style, false, &buttons_ptr);
01188 frame.layout(left,top,width,height);
01189 frame.draw();
01190
01191 const SDL_Rect progress_rect = {left+border,top+border,width-border*2,height-border*2};
01192 gui::progress_bar progress(disp.video());
01193 progress.set_location(progress_rect);
01194
01195 events::raise_draw_event();
01196 disp.flip();
01197
01198 network::statistics old_stats = get_stats(connection_num);
01199
01200 cfg.clear();
01201 for(;;) {
01202 const network::connection res = network::receive_data(cfg,connection_num,100);
01203 const network::statistics stats = get_stats(connection_num);
01204 if(stats.current_max != 0 && stats != old_stats) {
01205 old_stats = stats;
01206 progress.set_progress_percent((stats.current*100)/stats.current_max);
01207 std::ostringstream stream;
01208 stream << stats.current/1024 << "/" << stats.current_max/1024 << _("KB");
01209 progress.set_text(stream.str());
01210 }
01211
01212 events::raise_draw_event();
01213 disp.flip();
01214 events::pump();
01215
01216 if(res != 0) {
01217 return res;
01218 }
01219
01220
01221 if(cancel_button.pressed()) {
01222 return res;
01223 }
01224 }
01225 }
01226
01227 network::connection network_send_dialog(display& disp, const std::string& msg, config& cfg, network::connection connection_num)
01228 {
01229 return network_data_dialog(disp, msg, cfg, connection_num,
01230 network::get_send_stats);
01231 }
01232
01233 network::connection network_receive_dialog(display& disp, const std::string& msg, config& cfg, network::connection connection_num)
01234 {
01235 return network_data_dialog(disp, msg, cfg, connection_num,
01236 network::get_receive_stats);
01237 }
01238
01239 }
01240
01241 namespace {
01242
01243 class connect_waiter : public threading::waiter
01244 {
01245 public:
01246 connect_waiter(display& disp, gui::button& button) : disp_(disp), button_(button)
01247 {}
01248 ACTION process();
01249
01250 private:
01251 display& disp_;
01252 gui::button& button_;
01253 };
01254
01255 connect_waiter::ACTION connect_waiter::process()
01256 {
01257 events::raise_draw_event();
01258 disp_.flip();
01259 events::pump();
01260 if(button_.pressed()) {
01261 return ABORT;
01262 } else {
01263 return WAIT;
01264 }
01265 }
01266
01267 }
01268
01269 namespace dialogs
01270 {
01271
01272 network::connection network_connect_dialog(display& disp, const std::string& msg, const std::string& hostname, int port)
01273 {
01274 #ifdef USE_TINY_GUI
01275 const size_t width = 200;
01276 const size_t height = 20;
01277 #else
01278 const size_t width = 250;
01279 const size_t height = 20;
01280 #endif
01281 const int left = disp.w()/2 - width/2;
01282 const int top = disp.h()/2 - height/2;
01283
01284 const events::event_context dialog_events_context;
01285
01286 gui::button cancel_button(disp.video(),_("Cancel"));
01287 std::vector<gui::button*> buttons_ptr(1,&cancel_button);
01288
01289 gui::dialog_frame frame(disp.video(), msg, gui::dialog_frame::default_style, false, &buttons_ptr);
01290 frame.layout(left,top,width,height);
01291 frame.draw();
01292
01293 events::raise_draw_event();
01294 disp.flip();
01295
01296 connect_waiter waiter(disp,cancel_button);
01297 return network::connect(hostname,port,waiter);
01298 }
01299
01300 }