00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
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
00054
00055
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
00166
00167
00168 u = resources::units->find(loc);
00169 if (u != resources::units->end()) {
00170
00171 if (u->experience() < 81) {
00172
00173
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
00206
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
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
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
00291 if(preferences::ask_delete_saves()) {
00292 if(!gui2::tgame_delete::execute(disp_.video())) {
00293 return gui::CONTINUE_DIALOG;
00294 }
00295 }
00296
00297
00298 filter_.delete_item(menu_selection);
00299
00300
00301 savegame::delete_game(saves_[index].name());
00302
00303
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
00373
00374
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
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
00475 symbols["campaign_name"] = "(" + campaign_id + ")";
00476 }
00477 str << vgettext("Campaign: $campaign_name", symbols);
00478
00479
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
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
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
00551 format_string = _("%b %d");
00552 }
00553 } else {
00554
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 }
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
00619 if(show_replay != NULL) {
00620 lmenu.add_option(_("Show replay"), false,
00621
00622
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
00630
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
00649
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
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
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
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
00843
00844
00845
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
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
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
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
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
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>";
01023
01024 det.experience = 0;
01025 det.max_experience = t->experience_needed();
01026 det.xp_color = "<0,160,225>";
01027
01028
01029
01030
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>";
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
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
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 }
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 }