menu_events.cpp

Go to the documentation of this file.
00001 /* $Id: menu_events.cpp 26797 2008-05-23 17:37:37Z suokko $ */
00002 /*
00003    Copyright (C) 2006 - 2008 by Joerg Hinrichs <joerg.hinrichs@alice-dsl.de>
00004    wesnoth playturn Copyright (C) 2003 by David White <dave@whitevine.net>
00005    Part of the Battle for Wesnoth Project http://www.wesnoth.org/
00006 
00007    This program is free software; you can redistribute it and/or modify
00008    it under the terms of the GNU General Public License version 2
00009    or 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 //! @file menu_events.cpp
00017 //! Operations activated from menus/hotkeys while playing a game.
00018 //! E.g. Unitlist, status_table, save_game, save_map, chat, show_help, etc.
00019 
00020 #include "global.hpp"
00021 
00022 #include "construct_dialog.hpp"
00023 #include "dialogs.hpp"
00024 #include "foreach.hpp"
00025 #include "formula_ai.hpp"
00026 #include "game_display.hpp"
00027 #include "game_config.hpp"
00028 #include "game_errors.hpp"
00029 #include "game_events.hpp"
00030 #include "gettext.hpp"
00031 #include "help.hpp"
00032 #include "log.hpp"
00033 #include "marked-up_text.hpp"
00034 #include "menu_events.hpp"
00035 #include "playturn.hpp"
00036 #include "preferences_display.hpp"
00037 #include "replay.hpp"
00038 #include "sound.hpp"
00039 #include "team.hpp"
00040 #include "unit_display.hpp"
00041 #include "unit_types.hpp"
00042 #include "wml_separators.hpp"
00043 #include "util.hpp"
00044 #include "serialization/string_utils.hpp"
00045 
00046 #include <algorithm>
00047 #include <cassert>
00048 #include <sstream>
00049 
00050 #define ERR_NG LOG_STREAM(err, engine)
00051 #define LOG_NG LOG_STREAM(info, engine)
00052 #define DBG_NG LOG_STREAM(info, engine)
00053 
00054 namespace {
00055 
00056 void remove_old_auto_saves()
00057 {
00058     const std::string auto_save = _("Auto-Save");
00059     int countdown = preferences::autosavemax();
00060     if (countdown == preferences::INFINITE_AUTO_SAVES)
00061         return;
00062 
00063     std::vector<save_info> games = get_saves_list(NULL, &auto_save);
00064     for (std::vector<save_info>::iterator i = games.begin(); i != games.end(); i++) {
00065         if (countdown-- <= 0) {
00066             LOG_NG << "Deleting savegame '" << i->name << "'\n";
00067             delete_game(i->name);
00068         }
00069     }
00070 }
00071 
00072 std::vector<std::string> create_unit_table(const statistics::stats::str_int_map& m, unsigned int team)
00073 {
00074     std::vector<std::string> table;
00075     for(statistics::stats::str_int_map::const_iterator i = m.begin(); i != m.end(); ++i) {
00076         const unit_type_data::unit_type_map::const_iterator type = unit_type_data::types().find(i->first);
00077         if(type == unit_type_data::types().end()) {
00078             continue;
00079         }
00080 
00081         std::stringstream str;
00082 
00083         str << IMAGE_PREFIX << type->second.image();
00084 #ifndef LOW_MEM
00085         str << "~RC(" << type->second.flag_rgb() << ">" << team << ")";
00086 #endif
00087         str << COLUMN_SEPARATOR << type->second.type_name() << COLUMN_SEPARATOR << i->second << "\n";
00088         table.push_back(str.str());
00089     }
00090 
00091     return table;
00092 }
00093 
00094 class statistics_dialog : public gui::dialog
00095 {
00096 public:
00097     statistics_dialog(game_display &disp, const std::string& title, const unsigned int team,
00098         const std::string& player);
00099     ~statistics_dialog();
00100 protected:
00101     void action(gui::dialog_process_info &dp_info);
00102 private:
00103     void make_damage_line(std::vector<std::string>&,const std::string&,const long long&,const long long&,const long long&,const long long&);
00104     gui::dialog_button *detail_btn_;
00105     std::string player_name_;
00106     statistics::stats stats_;
00107     unsigned int team_num_;
00108     std::vector<int> unit_count_;
00109 };
00110 
00111 void statistics_dialog::action(gui::dialog_process_info &dp_info)
00112 {
00113     int sel = get_menu().selection();
00114     bool has_details = sel < 5 && sel >= 0 && unit_count_[sel] > 0;
00115     detail_btn_->enable(has_details);
00116     if(dp_info.double_clicked && has_details) {
00117         set_result(sel);
00118     } else if(dp_info.new_key_down && !dp_info.key_down) {
00119         set_result(gui::CLOSE_DIALOG);
00120     }
00121 
00122     // Prepare the sub-dialog for Statistic Details
00123     std::string title;
00124     std::vector<std::string> items_sub;
00125     switch(result()) {
00126     case gui::CLOSE_DIALOG:
00127         break;
00128     case 0:
00129         items_sub = create_unit_table(stats_.recruits, team_num_);
00130         title = _("Recruits");
00131         break;
00132     case 1:
00133         items_sub = create_unit_table(stats_.recalls, team_num_);
00134         title = _("Recalls");
00135         break;
00136     case 2:
00137         items_sub = create_unit_table(stats_.advanced_to, team_num_);
00138         title = _("Advancements");
00139         break;
00140     case 3:
00141         items_sub = create_unit_table(stats_.deaths, team_num_);
00142         title = _("Losses");
00143         break;
00144     case 4:
00145         items_sub = create_unit_table(stats_.killed, team_num_);
00146         //! @todo FIXME? Perhaps killed units shouldn't have the same team-color as your own.
00147         title = _("Kills");
00148         break;
00149     default:
00150         break;
00151     }
00152     if (items_sub.empty() == false) {
00153         gui::dialog d(get_display(), title + " (" + player_name_ + ")", "", gui::CLOSE_ONLY);
00154         d.set_menu(items_sub);
00155         d.show();
00156         dp_info.clear_buttons();
00157         set_result(gui::CONTINUE_DIALOG);
00158     }
00159 }
00160 
00161 statistics_dialog::statistics_dialog(game_display &disp, const std::string& title,
00162 const unsigned int team, const std::string& player)
00163 : dialog(disp, title, "", gui::NULL_DIALOG), player_name_(player),
00164 team_num_(team), unit_count_(5,0)
00165 {
00166     detail_btn_ = new gui::standard_dialog_button(disp.video(), _("Details"), 0 , false);
00167     add_button(detail_btn_, gui::dialog::BUTTON_EXTRA);
00168     add_button(new gui::standard_dialog_button(disp.video(), _("Close"), 1, true),
00169                 gui::dialog::BUTTON_STANDARD);
00170 
00171     stats_ = statistics::calculate_stats(0, team_num_);
00172     int n;
00173     std::vector<std::string> items;
00174     // Prepare the menu items
00175     {
00176         std::stringstream str;
00177         n = statistics::sum_str_int_map(stats_.recruits);
00178         unit_count_[0] = n;
00179         str << _("Recruits") << COLUMN_SEPARATOR << n;
00180         items.push_back(str.str());
00181     }
00182     {
00183         std::stringstream str;
00184         n = statistics::sum_str_int_map(stats_.recalls);
00185         unit_count_[1] = n;
00186         str << _("Recalls") << COLUMN_SEPARATOR << n;
00187         items.push_back(str.str());
00188     }
00189     {
00190         std::stringstream str;
00191         n = statistics::sum_str_int_map(stats_.advanced_to);
00192         unit_count_[2] = n;
00193         str << _("Advancements") << COLUMN_SEPARATOR << n;
00194         items.push_back(str.str());
00195     }
00196     {
00197         std::stringstream str;
00198         n = statistics::sum_str_int_map(stats_.deaths);
00199         unit_count_[3] = n;
00200         str << _("Losses") << COLUMN_SEPARATOR << n;
00201         items.push_back(str.str());
00202     }
00203     {
00204         std::stringstream str;
00205         n = statistics::sum_str_int_map(stats_.killed);
00206         unit_count_[4] = n;
00207         str << _("Kills") << COLUMN_SEPARATOR << n;
00208         items.push_back(str.str());
00209     }
00210     items.push_back("");
00211     {
00212         std::stringstream str;
00213         str << font::BOLD_TEXT << _("Damage")
00214             << COLUMN_SEPARATOR << _("Over All") << COLUMN_SEPARATOR
00215             << COLUMN_SEPARATOR
00216             << COLUMN_SEPARATOR << _("This Turn");
00217         items.push_back(str.str());
00218     }
00219     
00220     statistics_dialog::make_damage_line(items, _("Inflicted"), 
00221             stats_.damage_inflicted, 
00222             stats_.expected_damage_inflicted,
00223             stats_.turn_damage_inflicted,
00224             stats_.turn_expected_damage_inflicted);
00225     statistics_dialog::make_damage_line(items, _("Taken"), 
00226             stats_.damage_taken, 
00227             stats_.expected_damage_taken,
00228             stats_.turn_damage_taken,
00229             stats_.turn_expected_damage_taken);
00230     items.push_back("New stats:");
00231     
00232     statistics_dialog::make_damage_line(items, _("Inflicted"), 
00233             stats_.damage_inflicted, 
00234             stats_.new_expected_damage_inflicted,
00235             stats_.turn_damage_inflicted,
00236             stats_.new_turn_expected_damage_inflicted);
00237     statistics_dialog::make_damage_line(items, _("Taken"), 
00238             stats_.damage_taken, 
00239             stats_.new_expected_damage_taken,
00240             stats_.turn_damage_taken,
00241             stats_.new_turn_expected_damage_taken);
00242     set_menu(items);
00243 }
00244 
00245 statistics_dialog::~statistics_dialog()
00246 {
00247 }
00248 
00249 void statistics_dialog::make_damage_line(std::vector<std::string>& items, 
00250                      const std::string& header,
00251                      const long long& damage, 
00252                      const long long& expected,
00253                      const long long& turn_damage, 
00254                      const long long& turn_expected)
00255 {
00256     const int dsa = statistics::stats::desimal_shift * damage
00257                   - expected;
00258     const int dst = statistics::stats::desimal_shift * turn_damage
00259                   - turn_expected;
00260 
00261     std::stringstream str;
00262     str << header << COLUMN_SEPARATOR
00263         << damage << " / "
00264         << (expected/100 / (double)statistics::stats::desimal_shift * 100.0)
00265         << COLUMN_SEPARATOR
00266         << ((dsa > 0) ? "+" : "")
00267         << ((expected == 0) ? 0
00268                 : 100 * dsa / expected)
00269         << "%" << COLUMN_SEPARATOR
00270         << COLUMN_SEPARATOR
00271         << turn_damage << " / "
00272         << (turn_expected/100 / (double)statistics::stats::desimal_shift * 100.0)
00273         << COLUMN_SEPARATOR
00274         << ((dst > 0) ? "+" : "")
00275         << ((turn_expected == 0) ? 0
00276                 : 100 * dst / turn_expected)
00277         << "%";
00278     items.push_back(str.str());
00279 
00280 }
00281 
00282 } // end anonymous namespace
00283 
00284 namespace events{
00285 
00286     class delete_recall_unit : public gui::dialog_button_action
00287     {
00288     public:
00289         delete_recall_unit(game_display& disp, std::vector<unit>& units) : disp_(disp), units_(units) {}
00290     private:
00291         gui::dialog_button_action::RESULT button_pressed(int menu_selection);
00292 
00293         game_display& disp_;
00294         std::vector<unit>& units_;
00295     };
00296 
00297     gui::dialog_button_action::RESULT delete_recall_unit::button_pressed(int menu_selection)
00298     {
00299         const size_t index = size_t(menu_selection);
00300         if(index < units_.size()) {
00301             const unit& u = units_[index];
00302 
00303             //If the unit is of level > 1, or is close to advancing,
00304             //we warn the player about it
00305             std::stringstream message;
00306             if (u.upkeep() == 0) {
00307                 message << _("My lord, this unit is loyal and requires no upkeep! ") << (u.gender() == unit_race::MALE ? _("Do you really want to dismiss him?")
00308                         : _("Do you really want to dismiss her?"));
00309             } else if(u.level() > 1) {
00310                 message << _("My lord, this unit is an experienced one, having advanced levels! ") << (u.gender() == unit_race::MALE ? _("Do you really want to dismiss him?")
00311                         : _("Do you really want to dismiss her?"));
00312 
00313             } else if(u.experience() > u.max_experience()/2) {
00314                 message << _("My lord, this unit is close to advancing a level! ") << (u.gender() == unit_race::MALE ? _("Do you really want to dismiss him?")
00315                         : _("Do you really want to dismiss her?"));
00316             }
00317 
00318             if(!message.str().empty()) {
00319                 const int res = gui::dialog(disp_,"",message.str(),gui::YES_NO).show();
00320                 if(res != 0) {
00321                     return gui::CONTINUE_DIALOG;
00322                 }
00323             }
00324 
00325             units_.erase(units_.begin() + index);
00326             recorder.add_disband(index);
00327             return gui::DELETE_ITEM;
00328         } else {
00329             return gui::CONTINUE_DIALOG;
00330         }
00331     }
00332 
00333     menu_handler::menu_handler(game_display* gui, unit_map& units, std::vector<team>& teams,
00334         const config& level, const gamemap& map,
00335         const config& game_config, const gamestatus& status, game_state& gamestate,
00336         undo_list& undo_stack, undo_list& redo_stack) :
00337     gui_(gui), units_(units), teams_(teams), level_(level), map_(map),
00338         game_config_(game_config), status_(status), gamestate_(gamestate), undo_stack_(undo_stack),
00339         redo_stack_(redo_stack)
00340     {
00341     }
00342 
00343     menu_handler::~menu_handler()
00344     {
00345     }
00346     const undo_list& menu_handler::get_undo_list() const{
00347          return undo_stack_;
00348     }
00349 
00350     gui::floating_textbox& menu_handler::get_textbox(){
00351         return textbox_info_;
00352     }
00353 
00354     std::string menu_handler::get_title_suffix(int team_num)
00355     {
00356         int controlled_recruiters = 0;
00357         for(size_t i = 0; i < teams_.size(); ++i) {
00358             if(teams_[i].is_human() && !teams_[i].recruits().empty()
00359             && team_leader(i+1, units_) != units_.end()) {
00360             ++controlled_recruiters;
00361             }
00362         }
00363         std::stringstream msg;
00364         if(controlled_recruiters >= 2) {
00365             const unit_map::const_iterator leader = team_leader(team_num, units_);
00366             if(leader != units_.end() && !leader->second.name().empty()) {
00367                 msg << " (" << leader->second.name(); msg << ")";
00368             }
00369         }
00370         return msg.str();
00371     }
00372     void menu_handler::objectives(const unsigned int team_num)
00373     {
00374         dialogs::show_objectives(*gui_, level_, teams_[team_num - 1].objectives());
00375         teams_[team_num - 1].reset_objectives_changed();
00376     }
00377 
00378     void menu_handler::show_statistics(const unsigned int team_num)
00379     {
00380         // Current Player name
00381         const std::string player = teams_[team_num - 1].current_player();
00382         //add player's name to title of dialog
00383         std::stringstream title_str;
00384         title_str <<  _("Statistics") << " (" << player << ")";
00385         statistics_dialog stats_dialog(*gui_, title_str.str(), team_num, player);
00386         stats_dialog.show();
00387     }
00388 
00389     void menu_handler::unit_list()
00390     {
00391         const std::string heading = std::string(1,HEADING_PREFIX) +
00392                                     _("Type")          + COLUMN_SEPARATOR +
00393                                     _("Name")          + COLUMN_SEPARATOR +
00394                                     _("Level^Lv.")     + COLUMN_SEPARATOR +
00395                                     _("HP")            + COLUMN_SEPARATOR +
00396                                     _("XP")            + COLUMN_SEPARATOR +
00397                                     _("unit list^Traits") + COLUMN_SEPARATOR +
00398                                     _("Moves")         + COLUMN_SEPARATOR +
00399                                     _("Location^Loc.") + COLUMN_SEPARATOR +
00400                                     _("Status");
00401 
00402         gui::menu::basic_sorter sorter;
00403         sorter.set_alpha_sort(0).set_alpha_sort(1).set_numeric_sort(2).set_numeric_sort(3)
00404               .set_alpha_sort(4).set_numeric_sort(5).set_numeric_sort(6);
00405 
00406         std::vector<std::string> items;
00407         items.push_back(heading);
00408 
00409         std::vector<gamemap::location> locations_list;
00410         std::vector<unit> units_list;
00411 
00412         int selected = 0;
00413 
00414         for(unit_map::const_iterator i = units_.begin(); i != units_.end(); ++i) {
00415             if(i->second.side() != (gui_->viewing_team()+1))
00416                 continue;
00417 
00418             std::stringstream row;
00419             // If a unit is already selected on the map, we do the same in the unit list dialog
00420             if (gui_->selected_hex() == i->first) {
00421                  row << DEFAULT_ITEM;
00422                  selected = units_list.size();
00423             }
00424 //%%
00425             // If unit is leader, show name in special color, e.g. gold/silver
00426             //! @todo TODO: hero just has overlay "misc/hero-icon.png" - needs an ability to query
00427 
00428             if(i->second.can_recruit() ) {
00429 //              row << "<255,255,200>";
00430                 row << "<205,173,0>";   // gold3
00431             }
00432             row << i->second.type_name() << COLUMN_SEPARATOR;
00433             if(i->second.can_recruit() ) {
00434 //              row << "<255,255,200>";
00435                 row << "<205,173,0>";   // gold3
00436             }
00437             row << i->second.name()   << COLUMN_SEPARATOR;
00438 
00439             // Show units of level (0=gray, 1 normal, 2 bold, 2+ bold&wbright)
00440             const int level = i->second.level();
00441             if(level < 1) {
00442                 row << "<150,150,150>";
00443             } else if(level == 1) {
00444                 row << font::NORMAL_TEXT;
00445             } else if(level == 2) {
00446                 row << font::BOLD_TEXT;
00447             } if(i->second.level() > 2 ) {
00448                 row << font::BOLD_TEXT << "<255,255,255>";
00449             }
00450             row << level << COLUMN_SEPARATOR;
00451 
00452             // Display HP
00453             // see also unit_preview_pane in dialogs.cpp
00454             row << font::color2markup(i->second.hp_color());
00455             row << i->second.hitpoints()  << "/" << i->second.max_hitpoints() << COLUMN_SEPARATOR;
00456 
00457             // Display XP
00458             row << font::color2markup(i->second.xp_color());
00459             row << i->second.experience() << "/";
00460             if(i->second.can_advance()) {
00461                 row << i->second.max_experience();
00462             } else {
00463                 row << "-";
00464             }
00465             row << COLUMN_SEPARATOR;
00466 
00467             // TODO: show 'loyal' in green / xxx in red  //  how to handle translations ??
00468             row << i->second.traits_description() << COLUMN_SEPARATOR;
00469 
00470             // display move left (0=red, moved=yellow, not moved=green)
00471             if(i->second.movement_left() == 0) {
00472                 row << font::RED_TEXT;
00473             } else if(i->second.movement_left() < i->second.total_movement() ) {
00474                 row << "<255,255,0>";
00475             } else {
00476                 row << font::GREEN_TEXT;
00477             }
00478             row << i->second.movement_left() << "/" << i->second.total_movement() << COLUMN_SEPARATOR;
00479 
00480             const int def =  100 - i->second.defense_modifier(map_.get_terrain(i->first));
00481             int val = (game_config::defense_color_scale.size()-1) * def/100;
00482             row << rgb2highlight(game_config::defense_color_scale[val]);
00483             row << i->first << COLUMN_SEPARATOR;
00484 
00485             // show icons if unit is slowed, poisoned, stoned, invisible:
00486             if(utils::string_bool(i->second.get_state("stoned")))
00487                 row << IMAGE_PREFIX << "misc/stone.png"    << IMG_TEXT_SEPARATOR;
00488             if(utils::string_bool(i->second.get_state("slowed")))
00489                 row << IMAGE_PREFIX << "misc/slowed.png"   << IMG_TEXT_SEPARATOR;
00490             if(utils::string_bool(i->second.get_state("poisoned")))
00491                 row << IMAGE_PREFIX << "misc/poisoned.png" << IMG_TEXT_SEPARATOR;
00492 
00493             //! @todo FIXME: condition for "invisible" does not work
00494             //if(utils::string_bool(i->second.get_state("hides")))  // "hides" gives ability, not status
00495             if(utils::string_bool(i->second.get_state("invisible")))
00496                 row << IMAGE_PREFIX << "misc/invisible.png";
00497 //%%
00498             items.push_back(row.str());
00499 
00500             locations_list.push_back(i->first);
00501             units_list.push_back(i->second);
00502         }
00503 
00504         {
00505             dialogs::units_list_preview_pane unit_preview(*gui_, &map_, units_list);
00506             unit_preview.set_selection(selected);
00507 
00508             gui::dialog umenu(*gui_, _("Unit List"), "", gui::NULL_DIALOG);
00509             umenu.set_menu(items, &sorter);
00510             umenu.add_pane(&unit_preview);
00511             umenu.add_button(new gui::standard_dialog_button(gui_->video(), _("Scroll To"), 0, false),
00512                              gui::dialog::BUTTON_STANDARD);
00513             umenu.add_button(new gui::standard_dialog_button(gui_->video(), _("Close"), 1, true),
00514                              gui::dialog::BUTTON_STANDARD);
00515             umenu.set_basic_behavior(gui::OK_CANCEL);
00516             selected = umenu.show();
00517         } // this will kill the dialog before scrolling
00518 
00519         if(selected >= 0 && selected < int(locations_list.size())) {
00520             const gamemap::location& loc = locations_list[selected];
00521             gui_->scroll_to_tile(loc,game_display::WARP);
00522             gui_->select_hex(loc);
00523         }
00524     }
00525 
00526 namespace {
00527 class leader_scroll_dialog : public gui::dialog {
00528 public:
00529     leader_scroll_dialog(display &disp, const std::string &title, std::vector<bool> &leader_bools, int selected, gui::DIALOG_RESULT extra_result)
00530     : dialog(disp, title, "", gui::NULL_DIALOG), leader_bools_(leader_bools), extra_result_(extra_result)
00531     {
00532         scroll_btn_ = new gui::standard_dialog_button(disp.video(), _("Scroll To"), 0, false);
00533         scroll_btn_->enable(leader_bools[selected]);
00534         add_button(scroll_btn_, gui::dialog::BUTTON_STANDARD);
00535         add_button(new gui::standard_dialog_button(disp.video(),
00536             _("Close"), 1, true), gui::dialog::BUTTON_STANDARD);
00537     }
00538     void action(gui::dialog_process_info &info) {
00539         const bool leader_bool = leader_bools_[get_menu().selection()];
00540         scroll_btn_->enable(leader_bool);
00541         if(leader_bool && (info.double_clicked || (!info.key_down
00542         && (info.key[SDLK_RETURN] || info.key[SDLK_KP_ENTER])))) {
00543             set_result(get_menu().selection());
00544         } else if(!info.key_down && info.key[SDLK_ESCAPE]) {
00545             set_result(gui::CLOSE_DIALOG);
00546         } else if(!info.key_down && info.key[SDLK_SPACE]) {
00547             set_result(extra_result_);
00548         } else if(result() == gui::CONTINUE_DIALOG) {
00549             dialog::action(info);
00550         }
00551     }
00552 private:
00553     gui::standard_dialog_button *scroll_btn_;
00554     std::vector<bool> &leader_bools_;
00555     gui::DIALOG_RESULT extra_result_;
00556 };
00557 } //end anonymous namespace
00558     void menu_handler::status_table(int selected)
00559     {
00560         std::stringstream heading;
00561         heading << HEADING_PREFIX << _("Leader") << COLUMN_SEPARATOR << ' ' << COLUMN_SEPARATOR
00562                 << _("Team")         << COLUMN_SEPARATOR
00563                 << _("Gold")         << COLUMN_SEPARATOR
00564                 << _("Villages")     << COLUMN_SEPARATOR
00565                 << _("status^Units") << COLUMN_SEPARATOR
00566                 << _("Upkeep")       << COLUMN_SEPARATOR
00567                 << _("Income");
00568 
00569         gui::menu::basic_sorter sorter;
00570         sorter.set_redirect_sort(0,1).set_alpha_sort(1).set_alpha_sort(2).set_numeric_sort(3)
00571               .set_numeric_sort(4).set_numeric_sort(5).set_numeric_sort(6).set_numeric_sort(7);
00572 
00573         std::vector<std::string> items;
00574         std::vector<bool> leader_bools;
00575         items.push_back(heading.str());
00576 
00577         const team& viewing_team = teams_[gui_->viewing_team()];
00578 
00579         //if the player is under shroud or fog, they don't get
00580         //to see details about the other sides, only their own
00581         //side, allied sides and a ??? is shown to demonstrate
00582         //lack of information about the other sides But he see
00583         //all names with in colours
00584         for(size_t n = 0; n != teams_.size(); ++n) {
00585             if(teams_[n].is_empty()) {
00586                 continue;
00587             }
00588 
00589             const bool known = viewing_team.knows_about_team(n);
00590             const bool enemy = viewing_team.is_enemy(n+1);
00591 
00592             std::stringstream str;
00593 
00594             const team_data data = calculate_team_data(teams_[n],n+1,units_);
00595 
00596             const unit_map::const_iterator leader = team_leader(n+1,units_);
00597             //output the number of the side first, and this will
00598             //cause it to be displayed in the correct colour
00599             if(leader != units_.end()) {
00600 
00601                 // Add leader image. If it's fogged
00602                 // show only a random leader image.
00603                 if (known || game_config::debug) {
00604                     str << IMAGE_PREFIX << leader->second.absolute_image();
00605                     leader_bools.push_back(true);
00606                 } else {
00607                     str << IMAGE_PREFIX << std::string("unknown-unit.png");
00608                     leader_bools.push_back(false);
00609                 }
00610 
00611 #ifndef LOW_MEM
00612                 str << "~RC(" << leader->second.team_color() << ">" << team::get_side_colour_index(n+1) << ")";
00613 #endif
00614             } else {
00615                 leader_bools.push_back(false);
00616             }
00617             str << COLUMN_SEPARATOR << team::get_side_highlight(n)
00618                 << teams_[n].current_player() << COLUMN_SEPARATOR
00619                 << (data.teamname.empty() ? teams_[n].team_name() : data.teamname)
00620                 << COLUMN_SEPARATOR;
00621 
00622             if(!known && !game_config::debug) {
00623                 // We don't spare more info (only name)
00624                 // so let's go on next side ...
00625                 items.push_back(str.str());
00626                 continue;
00627             }
00628 
00629             if(game_config::debug) {
00630                 str << data.gold << COLUMN_SEPARATOR;
00631             } else if(enemy && viewing_team.uses_fog()) {
00632                 str << ' ' << COLUMN_SEPARATOR;
00633             } else {
00634                 str << data.gold << COLUMN_SEPARATOR;
00635             }
00636             str << data.villages << COLUMN_SEPARATOR
00637                 << data.units << COLUMN_SEPARATOR << data.upkeep << COLUMN_SEPARATOR
00638                 << (data.net_income < 0 ? font::BAD_TEXT : font::NULL_MARKUP) << data.net_income;
00639 
00640             items.push_back(str.str());
00641         }
00642 
00643         int result = 0;
00644         {
00645             leader_scroll_dialog slist(*gui_, _("Current Status"), leader_bools, selected, gui::DIALOG_FORWARD);
00646             slist.add_button(new gui::dialog_button(gui_->video(), _("More >"),
00647                                  gui::button::TYPE_PRESS, gui::DIALOG_FORWARD),
00648                              gui::dialog::BUTTON_EXTRA_LEFT);
00649             slist.set_menu(items, &sorter);
00650             slist.get_menu().move_selection(selected);
00651             result = slist.show();
00652             selected = slist.get_menu().selection();
00653         } // this will kill the dialog before scrolling
00654 
00655         if (result >= 0)
00656             gui_->scroll_to_leader(units_, selected+1);
00657         else if (result == gui::DIALOG_FORWARD)
00658             scenario_settings_table(selected);
00659     }
00660 
00661     void menu_handler::scenario_settings_table(int selected)
00662     {
00663         std::stringstream heading;
00664         heading << HEADING_PREFIX << _("scenario settings^Leader") << COLUMN_SEPARATOR
00665                 << COLUMN_SEPARATOR
00666                 << _("scenario settings^Side")              << COLUMN_SEPARATOR
00667                 << _("scenario settings^Start\nGold")       << COLUMN_SEPARATOR
00668                 << _("scenario settings^Base\nIncome")      << COLUMN_SEPARATOR
00669                 << _("scenario settings^Gold Per\nVillage") << COLUMN_SEPARATOR
00670                 << _("scenario settings^Fog")               << COLUMN_SEPARATOR
00671                 << _("scenario settings^Shroud");
00672 
00673         gui::menu::basic_sorter sorter;
00674         sorter.set_redirect_sort(0,1).set_alpha_sort(1).set_numeric_sort(2)
00675               .set_numeric_sort(3).set_numeric_sort(4).set_numeric_sort(5)
00676               .set_alpha_sort(6).set_alpha_sort(7);
00677 
00678         std::vector<std::string> items;
00679         std::vector<bool> leader_bools;
00680         items.push_back(heading.str());
00681 
00682         const team& viewing_team = teams_[gui_->viewing_team()];
00683 
00684         for(size_t n = 0; n != teams_.size(); ++n) {
00685             if(teams_[n].is_empty()) {
00686                 continue;
00687             }
00688 
00689             std::stringstream str;
00690             const unit_map::const_iterator leader = team_leader(n+1, units_);
00691 
00692             if(leader != units_.end()) {
00693                 // Add leader image. If it's fogged
00694                 // show only a random leader image.
00695                 if (viewing_team.knows_about_team(n) || game_config::debug) {
00696                     str << IMAGE_PREFIX << leader->second.absolute_image();
00697                     leader_bools.push_back(true);
00698                 } else {
00699                     str << IMAGE_PREFIX << std::string("unknown-unit.png");
00700                     leader_bools.push_back(false);
00701                 }
00702 #ifndef LOW_MEM
00703                 str << "~RC(" << leader->second.team_color() << ">"
00704                     << team::get_side_colour_index(n+1) << ")";
00705 #endif
00706             } else {
00707                 leader_bools.push_back(false);
00708             }
00709 
00710             str << COLUMN_SEPARATOR << team::get_side_highlight(n)
00711                 << teams_[n].current_player() << COLUMN_SEPARATOR
00712                 << n + 1 << COLUMN_SEPARATOR
00713                 << teams_[n].start_gold() << COLUMN_SEPARATOR
00714                 << teams_[n].base_income() << COLUMN_SEPARATOR
00715                 << teams_[n].village_gold() << COLUMN_SEPARATOR
00716                 << (teams_[n].uses_fog()    ? _("yes") : _("no")) << COLUMN_SEPARATOR
00717                 << (teams_[n].uses_shroud() ? _("yes") : _("no")) << COLUMN_SEPARATOR;
00718 
00719             items.push_back(str.str());
00720         }
00721 
00722         int result = 0;
00723         {
00724             leader_scroll_dialog slist(*gui_, _("Scenario Settings"), leader_bools, selected, gui::DIALOG_BACK);
00725             slist.set_menu(items, &sorter);
00726             slist.get_menu().move_selection(selected);
00727             slist.add_button(new gui::dialog_button(gui_->video(), _(" < Back"),
00728                                  gui::button::TYPE_PRESS, gui::DIALOG_BACK),
00729                              gui::dialog::BUTTON_EXTRA_LEFT);
00730             result = slist.show();
00731             selected = slist.get_menu().selection();
00732         } // this will kill the dialog before scrolling
00733 
00734         if (result >= 0)
00735             gui_->scroll_to_leader(units_, selected+1);
00736         else if (result == gui::DIALOG_BACK)
00737             status_table(selected);
00738     }
00739 
00740     void menu_handler::save_game(const std::string& message, gui::DIALOG_TYPE dialog_type,
00741         const bool has_exit_button, const bool replay)
00742     {
00743         std::stringstream stream;
00744 
00745         const std::string ellipsed_name = font::make_text_ellipsis(gamestate_.label,
00746                 font::SIZE_NORMAL, 200);
00747         if (replay) {
00748             stream << ellipsed_name << " " << _("replay");
00749         } else {
00750             stream << ellipsed_name << " " << _("Turn")
00751                    << " " << status_.turn();
00752         }
00753         std::string label = stream.str();
00754         if(dialog_type == gui::NULL_DIALOG && message != "") {
00755             label = message;
00756         }
00757 
00758         label.erase(std::remove_if(label.begin(), label.end(),
00759                     dialogs::is_illegal_file_char), label.end());
00760 
00761         const int res = dialog_type == gui::NULL_DIALOG ? 0
00762             : dialogs::get_save_name(*gui_,message, _("Name: "), &label,dialog_type, "", has_exit_button);
00763 
00764         if(res == 0) {
00765             config snapshot;
00766             if (!replay)
00767                 write_game_snapshot(snapshot);
00768             try {
00769                 recorder.save_game(label, snapshot, gamestate_.starting_pos);
00770                 if(dialog_type != gui::NULL_DIALOG) {
00771                     gui::message_dialog(*gui_,_("Saved"),_("The game has been saved")).show();
00772                 }
00773             } catch(game::save_game_failed&) {
00774                 gui::message_dialog to_show(*gui_,_("Error"),_("The game could not be saved"));
00775                 to_show.show();
00776                 //do not bother retrying, since the user can just try to save the game again
00777             };
00778         } else if(res == 2) {
00779             throw end_level_exception(QUIT);
00780         }
00781     }
00782 
00783     void menu_handler::save_map()
00784     {
00785         std::string input_name = get_dir(get_dir(get_user_data_dir() + "/editor") + "/maps/");
00786         int res = 0;
00787         int overwrite = 1;
00788         do {
00789             res = dialogs::show_file_chooser_dialog(*gui_, input_name, _("Save the Map As"));
00790             if (res == 0) {
00791 
00792                 if (file_exists(input_name)) {
00793                     overwrite = gui::dialog(*gui_, "",
00794                         _("The map already exists. Do you want to overwrite it?"),
00795                         gui::YES_NO).show();
00796                 }
00797                 else
00798                     overwrite = 0;
00799             }
00800         } while (res == 0 && overwrite != 0);
00801 
00802         // Try to save the map, if it fails we reset the filename.
00803         if (res == 0) {
00804             try {
00805                 write_file(input_name, map_.write());
00806                 gui::message_dialog(*gui_, "", _("Map saved.")).show();
00807             } catch (io_exception& e) {
00808                 utils::string_map symbols;
00809                 symbols["msg"] = e.what();
00810                 const std::string msg = vgettext("Could not save the map: $msg",symbols);
00811                 gui::message_dialog(*gui_, "", msg).show();
00812             }
00813         }
00814     }
00815 
00816     void menu_handler::write_game_snapshot(config& start) const
00817     {
00818         start.values = level_.values;
00819 
00820         start["snapshot"] = "yes";
00821 
00822         std::stringstream buf;
00823         buf << gui_->playing_team();
00824         start["playing_team"] = buf.str();
00825 
00826         for(std::vector<team>::const_iterator t = teams_.begin(); t != teams_.end(); ++t) {
00827             const unsigned int side_num = t - teams_.begin() + 1;
00828 
00829             config& side = start.add_child("side");
00830             t->write(side);
00831             side["no_leader"] = "yes";
00832             buf.str(std::string());
00833             buf << side_num;
00834             side["side"] = buf.str();
00835 
00836             //current visible units
00837             for(unit_map::const_iterator i = units_.begin(); i != units_.end(); ++i) {
00838                 if(i->second.side() == side_num) {
00839                     config& u = side.add_child("unit");
00840                     i->first.write(u);
00841                     i->second.write(u);
00842                 }
00843             }
00844             //recall list
00845             {
00846                 for(std::map<std::string, player_info>::const_iterator i=gamestate_.players.begin();
00847                 i!=gamestate_.players.end(); ++i) {
00848                     for(std::vector<unit>::const_iterator j = i->second.available_units.begin();
00849                         j != i->second.available_units.end(); ++j) {
00850                         if (j->side() == side_num){
00851                             config& u = side.add_child("unit");
00852                             j->write(u);
00853                         }
00854                     }
00855                 }
00856             }
00857         }
00858 
00859         status_.write(start);
00860         game_events::write_events(start);
00861 
00862         // Write terrain_graphics data in snapshot, too
00863         const config::child_list& terrains = level_.get_children("terrain_graphics");
00864         for(config::child_list::const_iterator tg = terrains.begin();
00865                 tg != terrains.end(); ++tg) {
00866 
00867             start.add_child("terrain_graphics", **tg);
00868         }
00869 
00870         sound::write_music_play_list(start);
00871 
00872         write_game(gamestate_, start, WRITE_SNAPSHOT_ONLY);
00873 
00874         /*
00875         // 29.11.07, YogiHH: I can't see why this is needed at all and it
00876         // breaks some savegame functionality. Probably the original reasons
00877         // for putting this in are no longer valid.
00878 
00879         // Clobber gold values to make sure the snapshot uses the values
00880         // in [side] instead.
00881         const config::child_list& players=start.get_children("player");
00882         for(config::child_list::const_iterator pi=players.begin();
00883             pi!=players.end(); ++pi) {
00884             (**pi)["gold"] = "-1000000";
00885         }
00886         */
00887         //write out the current state of the map
00888         start["map_data"] = map_.write();
00889 
00890         gui_->labels().write(start);
00891     }
00892 
00893     void menu_handler::autosave(const std::string &label, unsigned turn, const config &starting_pos) const
00894     {
00895         if(game_config::disable_autosave)
00896             return;
00897 
00898         Uint32 start, end;
00899         start = SDL_GetTicks();
00900         config snapshot;
00901         std::string savename;
00902         if (label.empty())
00903             savename = _("Auto-Save");
00904         else
00905             savename = label + "-" + _("Auto-Save") + lexical_cast<std::string>(turn);
00906         write_game_snapshot(snapshot);
00907         try {
00908             recorder.save_game(savename, snapshot, starting_pos);
00909         } catch(game::save_game_failed&) {
00910             gui::message_dialog(*gui_,"",_("Could not auto save the game. Please save the game manually.")).show();
00911             //do not bother retrying, since the user can just save the game
00912         }
00913         end = SDL_GetTicks();
00914         LOG_NG << "Milliseconds to save " << savename << ": " << end - start << "\n";
00915 
00916         remove_old_auto_saves();
00917     }
00918 
00919     void menu_handler::load_game(){
00920         bool show_replay = false;
00921         bool cancel_orders = false;
00922         const std::string game = dialogs::load_game_dialog(*gui_, game_config_, &show_replay, &cancel_orders);
00923         if(game != "") {
00924             throw game::load_game_exception(game,show_replay,cancel_orders);
00925         }
00926     }
00927 
00928     void menu_handler::preferences()
00929     {
00930         preferences::show_preferences_dialog(*gui_, game_config_);
00931         gui_->redraw_everything();
00932     }
00933 
00934     void menu_handler::show_chat_log()
00935     {
00936         std::string text = recorder.build_chat_log(
00937             is_observer() ? game_config::observer_team_name
00938                           : teams_[gui_->viewing_team()].team_name());
00939         gui::show_dialog(*gui_,NULL,_("Chat Log"),"",gui::CLOSE_ONLY,NULL,NULL,"",&text);
00940     }
00941 
00942     void menu_handler::show_help()
00943     {
00944         help::show_help(*gui_);
00945     }
00946 
00947     void menu_handler::speak()
00948     {
00949         textbox_info_.show(gui::TEXTBOX_MESSAGE,_("Message:"),
00950             has_friends() ? is_observer() ? _("Send to observers only") : _("Send to allies only")
00951                           : "", preferences::message_private(), *gui_);
00952     }
00953 
00954     void menu_handler::whisper()
00955     {
00956         preferences::set_message_private(true);
00957         speak();
00958     }
00959 
00960     void menu_handler::shout()
00961     {
00962         preferences::set_message_private(false);
00963         speak();
00964     }
00965 
00966     bool menu_handler::has_friends() const
00967     {
00968         if(is_observer()) {
00969             return !gui_->observers().empty();
00970         }
00971 
00972         for(size_t n = 0; n != teams_.size(); ++n) {
00973             if(n != gui_->viewing_team() && teams_[gui_->viewing_team()].team_name() == teams_[n].team_name() && teams_[n].is_network()) {
00974                 return true;
00975             }
00976         }
00977 
00978         return false;
00979     }
00980 
00981     bool menu_handler::has_team() const
00982     {
00983         if(is_observer()) {
00984             return false;
00985         }
00986 
00987         for(size_t n = 0; n != teams_.size(); ++n) {
00988             if(n != gui_->viewing_team() && teams_[gui_->viewing_team()].team_name() == teams_[n].team_name()) {
00989                 return true;
00990             }
00991         }
00992 
00993         return false;
00994     }
00995 
00996     void menu_handler::recruit(const bool browse, const unsigned int team_num, const gamemap::location& last_hex)
00997     {
00998         if(browse)
00999             return;
01000 
01001         team& current_team = teams_[team_num-1];
01002 
01003         std::vector<const unit_type*> sample_units;
01004 
01005         gui_->draw(); //clear the old menu
01006         std::vector<std::string> item_keys;
01007         std::vector<std::string> items;
01008         const std::set<std::string>& recruits = current_team.recruits();
01009         for(std::set<std::string>::const_iterator it = recruits.begin(); it != recruits.end(); ++it) {
01010             const std::map<std::string,unit_type>::const_iterator
01011                     u_type = unit_type_data::types().find(*it);
01012             if(u_type == unit_type_data::types().end()) {
01013                 ERR_NG << "could not find unit '" << *it << "'\n";
01014                 return;
01015             }
01016 
01017             item_keys.push_back(*it);
01018 
01019             const unit_type* type = &u_type->second;
01020 
01021             //display units that we can't afford to recruit in red
01022             const char prefix = (type->cost() > current_team.gold() ? font::BAD_TEXT : font::NULL_MARKUP);
01023 
01024             std::stringstream description;
01025             description << font::IMAGE << type->image();
01026 #ifndef LOW_MEM
01027             description << "~RC(" << type->flag_rgb() << ">" << team::get_side_colour_index(team_num) << ")";
01028 #endif
01029             description << COLUMN_SEPARATOR << font::LARGE_TEXT << prefix << type->type_name() << "\n"
01030                     << prefix << type->cost() << " " << sngettext("unit^Gold", "Gold", type->cost());
01031 
01032             items.push_back(description.str());
01033             sample_units.push_back(type);
01034         }
01035 
01036         if(sample_units.empty()) {
01037             gui::message_dialog to_show(*gui_,"",_("You have no units available to recruit."));
01038             to_show.show();
01039             return;
01040         }
01041 
01042         int recruit_res = 0;
01043 
01044         {
01045             dialogs::unit_types_preview_pane unit_preview(*gui_,&map_,sample_units,team_num);
01046             std::vector<gui::preview_pane*> preview_panes;
01047             preview_panes.push_back(&unit_preview);
01048 
01049             gui::dialog rmenu(*gui_,_("Recruit") + get_title_suffix(team_num),
01050                       _("Select unit:") + std::string("\n"),
01051                       gui::OK_CANCEL,
01052                       gui::dialog::default_style);
01053             rmenu.add_button(new help::help_button(*gui_,"recruit_and_recall"),
01054                 gui::dialog::BUTTON_HELP);
01055             rmenu.set_menu(items);
01056             rmenu.set_panes(preview_panes);
01057             recruit_res = rmenu.show();
01058         }
01059 
01060         if(recruit_res != -1) {
01061             do_recruit(item_keys[recruit_res], team_num, last_hex);
01062         }
01063     }
01064 
01065     void menu_handler::repeat_recruit(const unsigned int team_num, const gamemap::location& last_hex)
01066     {
01067         if(last_recruit_.empty() == false)
01068             do_recruit(last_recruit_, team_num, last_hex);
01069     }
01070 
01071     void menu_handler::do_recruit(const std::string& name, const int unsigned team_num, const gamemap::location& last_hex)
01072     {
01073         team& current_team = teams_[team_num-1];
01074 
01075         //search for the unit to be recruited in recruits
01076         int recruit_num = 0;
01077         const std::set<std::string>& recruits = current_team.recruits();
01078         for(std::set<std::string>::const_iterator r = recruits.begin(); ; ++r) {
01079             if (r == recruits.end()) {
01080                 return;
01081             }
01082 
01083             if (name == *r) {
01084                 break;
01085             }
01086             ++recruit_num;
01087         }
01088 
01089         const std::map<std::string,unit_type>::const_iterator
01090                 u_type = unit_type_data::types().find(name);
01091         assert(u_type != unit_type_data::types().end());
01092 
01093         if(u_type->second.cost() > current_team.gold()) {
01094             gui::message_dialog(*gui_,"",
01095                  _("You don't have enough gold to recruit that unit")).show();
01096         } else {
01097             last_recruit_ = name;
01098 
01099             //create a unit with traits
01100             recorder.add_recruit(recruit_num, last_hex);
01101             unit new_unit(&units_,&map_,&status_,&teams_,&(u_type->second),team_num,true);
01102             gamemap::location loc = last_hex;
01103             const std::string& msg = recruit_unit(map_,team_num,units_,new_unit,loc,false,(gui_!=NULL));
01104             if(msg.empty()) {
01105                 current_team.spend_gold(u_type->second.cost());
01106                 statistics::recruit_unit(new_unit);
01107 
01108                 //MP_COUNTDOWN grant time bonus for recruiting
01109                 current_team.set_action_bonus_count(1 + current_team.action_bonus_count());
01110 
01111                 redo_stack_.clear();
01112                 assert(new_unit.type());
01113 
01114                 // Dissallow undoing of recruits. Can be enabled again once the unit's
01115                 // description= key doesn't use random anymore.
01116                 const bool shroud_cleared = clear_shroud(team_num);
01117                 if(shroud_cleared || new_unit.type()->genders().size() > 1
01118                         || new_unit.type()->has_random_traits()) {
01119                     clear_undo_stack(team_num);
01120                 } else {
01121                     undo_stack_.push_back(undo_action(new_unit,loc,RECRUIT_POS));
01122                 }
01123 
01124                 gui_->recalculate_minimap();
01125                 gui_->invalidate_game_status();
01126                 gui_->invalidate_all();
01127                 recorder.add_checksum_check(loc);
01128             } else {
01129                 recorder.undo();
01130                 gui::message_dialog(*gui_,"",msg).show();
01131             }
01132         }
01133     }
01134 
01135     void menu_handler::recall(const unsigned int team_num, const gamemap::location& last_hex)
01136     {
01137         player_info *player = gamestate_.get_player(teams_[team_num-1].save_id());
01138         if(!player) {
01139             ERR_NG << "cannot recall a unit for side " << team_num
01140                 << ", which has no recall list!\n";
01141             return;
01142         }
01143 
01144         team& current_team = teams_[team_num-1];
01145         std::vector<unit>& recall_list = player->available_units;
01146 
01147         //sort the available units into order by value
01148         //so that the most valuable units are shown first
01149         sort_units(recall_list);
01150 
01151         gui_->draw(); //clear the old menu
01152 
01153         if(utils::string_bool(level_["disallow_recall"])) {
01154             gui::message_dialog(*gui_,"",_("You are separated from your soldiers and may not recall them")).show();
01155         } else if(recall_list.empty()) {
01156             gui::message_dialog(*gui_,"",_("There are no troops available to recall\n(You must have veteran survivors from a previous scenario)")).show();
01157         } else {
01158             std::vector<std::string> options;
01159 
01160             std::ostringstream heading;
01161             heading << HEADING_PREFIX << COLUMN_SEPARATOR << _("Type")
01162                     << COLUMN_SEPARATOR << _("Name")
01163                     << COLUMN_SEPARATOR << _("Level")
01164                     << COLUMN_SEPARATOR << _("XP");
01165 
01166             gui::menu::basic_sorter sorter;
01167             sorter.set_alpha_sort(1).set_alpha_sort(2).set_id_sort(3).set_numeric_sort(4);
01168 
01169             options.push_back(heading.str());
01170 
01171             for(std::vector<unit>::const_iterator u = recall_list.begin(); u != recall_list.end(); ++u) {
01172                 std::stringstream option;
01173                 const std::string& name = u->name().empty() ? "-" : u->name();
01174 
01175                 option << IMAGE_PREFIX << u->absolute_image();
01176 #ifndef LOW_MEM
01177                 option << "~RC("  << u->team_color() << ">" << team::get_side_colour_index(team_num) << ")";
01178 #endif
01179                 option << COLUMN_SEPARATOR
01180                     << u->type_name() << COLUMN_SEPARATOR
01181                     << name << COLUMN_SEPARATOR
01182                     << u->level() << COLUMN_SEPARATOR
01183                     << u->experience() << "/";
01184 
01185                 if(u->can_advance() == false) {
01186                     option << "-";
01187                 } else {
01188                     option << u->max_experience();
01189                 }
01190 
01191                 options.push_back(option.str());
01192             }
01193 
01194             delete_recall_unit recall_deleter(*gui_,recall_list);
01195             gui::dialog_button_info delete_button(&recall_deleter,_("Dismiss Unit"));
01196             int res = 0;
01197 
01198             {
01199                 dialogs::units_list_preview_pane unit_preview(*gui_,&map_,recall_list);
01200                 gui::dialog rmenu(*gui_,_("Recall") + get_title_suffix(team_num),
01201                           _("Select unit:") + std::string("\n"),
01202                           gui::OK_CANCEL,
01203                           gui::dialog::default_style);
01204                 rmenu.add_button(new help::help_button(*gui_,"recruit_and_recall"),
01205                     gui::dialog::BUTTON_HELP);
01206                 rmenu.set_menu(options, &sorter);
01207                 rmenu.add_pane(&unit_preview);
01208                 rmenu.add_button(delete_button);
01209                 res = rmenu.show();
01210             }
01211 
01212             if(res >= 0) {
01213                 if(current_team.gold() < game_config::recall_cost) {
01214                     std::stringstream msg;
01215                     utils::string_map i18n_symbols;
01216                     i18n_symbols["cost"] = lexical_cast<std::string>(game_config::recall_cost);
01217                     msg << vngettext("You must have at least 1 gold piece to recall a unit",
01218                         "You must have at least $cost gold pieces to recall a unit",
01219                         game_config::recall_cost,
01220                         i18n_symbols);
01221                     gui::dialog(*gui_,"",msg.str()).show();
01222                 } else {
01223                     LOG_NG << "recall index: " << res << "\n";
01224                     unit& un = recall_list[res];
01225                     gamemap::location loc = last_hex;
01226                     recorder.add_recall(res,loc);
01227                     un.set_game_context(&units_,&map_,&status_,&teams_);
01228                     const std::string err = recruit_unit(map_,team_num,units_,un,loc,true,(gui_!=NULL));
01229                     if(!err.empty()) {
01230                         recorder.undo();
01231                         gui::dialog(*gui_,"",err,gui::OK_ONLY).show();
01232                     } else {
01233                         statistics::recall_unit(un);
01234                         current_team.spend_gold(game_config::recall_cost);
01235 
01236                         const bool shroud_cleared = clear_shroud(team_num);
01237                         if (shroud_cleared) {
01238                             clear_undo_stack(team_num);
01239                         } else {
01240                             undo_stack_.push_back(undo_action(un,loc,res));
01241                         }
01242 
01243                         redo_stack_.clear();
01244 
01245                         recall_list.erase(recall_list.begin()+res);
01246                         gui_->invalidate_game_status();
01247                         gui_->invalidate_all();
01248                         recorder.add_checksum_check(loc);
01249                     }
01250                 }
01251             }
01252         }
01253     }
01254     void menu_handler::undo(const unsigned int team_num)
01255     {
01256         if(undo_stack_.empty())
01257             return;
01258 
01259         const events::command_disabler disable_commands;
01260 
01261         undo_action& action = undo_stack_.back();
01262         if(action.is_recall()) {
01263             player_info* const player = gamestate_.get_player(teams_[team_num - 1].save_id());
01264 
01265             if(player == NULL) {
01266                 ERR_NG << "trying to undo a recall for side " << team_num
01267                     << ", which has no recall list!\n";
01268             } else {
01269                 // Undo a recall action
01270                 if(units_.count(action.recall_loc) == 0) {
01271                     return;
01272                 }
01273 
01274                 const unit& un = units_.find(action.recall_loc)->second;
01275                 statistics::un_recall_unit(un);
01276                 teams_[team_num - 1].spend_gold(-game_config::recall_cost);
01277 
01278                 std::vector<unit>& recall_list = player->available_units;
01279                 recall_list.insert(recall_list.begin()+action.recall_pos,un);
01280                 // invalidate before erasing allow us
01281                 // to also do the ovelerlapped hexes
01282                 gui_->invalidate(action.recall_loc);
01283                 units_.erase(action.recall_loc);
01284                 gui_->draw();
01285             }
01286         } else if(action.is_recruit()) {
01287             // Undo a recruit action
01288             team& current_team = teams_[team_num-1];
01289             if(units_.count(action.recall_loc) == 0) {
01290                 return;
01291             }
01292 
01293             const unit& un = units_.find(action.recall_loc)->second;
01294             statistics::un_recruit_unit(un);
01295             assert(un.type());
01296             current_team.spend_gold(-un.type()->cost());
01297 
01298             //MP_COUNTDOWN take away recruit bonus
01299             if(action.countdown_time_bonus)
01300             {
01301                 teams_[team_num-1].set_action_bonus_count(teams_[team_num-1].action_bonus_count() - 1);
01302             }
01303 
01304             // invalidate before erasing allow us
01305             // to also do the ovelerlapped hexes
01306             gui_->invalidate(action.recall_loc);
01307             units_.erase(action.recall_loc);
01308             gui_->draw();
01309         } else {
01310             // Undo a move action
01311             const int starting_moves = action.starting_moves;
01312             std::vector<gamemap::location> route = action.route;
01313             std::reverse(route.begin(),route.end());
01314             const unit_map::iterator u = units_.find(route.front());
01315             const unit_map::iterator u_end = units_.find(route.back());
01316             if(u == units_.end() || u_end != units_.end()) {
01317                 //this can actually happen if the scenario designer has abused the [allow_undo] command
01318                 ERR_NG << "Illegal 'undo' found. Possible abuse of [allow_undo]?\n";
01319                 return;
01320             }
01321 
01322             if(map_.is_village(route.front())) {
01323                 get_village(route.front(),*gui_,teams_,action.original_village_owner,units_);
01324                 //MP_COUNTDOWN take away capture bonus
01325                 if(action.countdown_time_bonus)
01326                 {
01327                     teams_[team_num-1].set_action_bonus_count(teams_[team_num-1].action_bonus_count() - 1);
01328                 }
01329             }
01330 
01331             action.starting_moves = u->second.movement_left();
01332 
01333             unit_display::move_unit(route,u->second,teams_);
01334             std::pair<gamemap::location,unit> *up = units_.extract(u->first);
01335             up->second.set_goto(gamemap::location());
01336             up->second.set_movement(starting_moves);
01337             up->first = route.back();
01338             units_.add(up);
01339             unit::clear_status_caches();
01340             up->second.set_standing(up->first);
01341             gui_->invalidate(route.back());
01342             gui_->draw();
01343         }
01344 
01345         gui_->invalidate_unit();
01346         gui_->invalidate_game_status();
01347 
01348         redo_stack_.push_back(action);
01349         undo_stack_.pop_back();
01350 
01351         recorder.undo();
01352 
01353         const bool shroud_cleared = clear_shroud(team_num);
01354 
01355         if(shroud_cleared) {
01356             gui_->recalculate_minimap();
01357         } else {
01358             gui_->redraw_minimap();
01359         }
01360     }
01361 
01362     void menu_handler::redo(const unsigned int team_num)
01363     {
01364         if(redo_stack_.empty())
01365             return;
01366 
01367         const events::command_disabler disable_commands;
01368 
01369         undo_action& action = redo_stack_.back();
01370         if(action.is_recall()) {
01371             player_info *player=gamestate_.get_player(teams_[team_num - 1].save_id());
01372             if(!player) {
01373                 ERR_NG << "trying to redo a recall for side " << team_num
01374                     << ", which has no recall list!\n";
01375             } else {
01376                 // Redo recall
01377                 std::vector<unit>& recall_list = player->available_units;
01378                 unit un = recall_list[action.recall_pos];
01379 
01380                 recorder.add_recall(action.recall_pos,action.recall_loc);
01381                 un.set_game_context(&units_,&map_,&status_,&teams_);
01382                 const std::string& msg = recruit_unit(map_,team_num,units_,un,action.recall_loc,true,(gui_!=NULL));
01383                 if(msg.empty()) {
01384                     statistics::recall_unit(un);
01385                     teams_[team_num - 1].spend_gold(game_config::recall_cost);
01386                     recall_list.erase(recall_list.begin()+action.recall_pos);
01387 
01388                     gui_->invalidate(action.recall_loc);
01389                     gui_->draw();
01390                     recorder.add_checksum_check(action.recall_loc);
01391                 } else {
01392                     recorder.undo();
01393                     gui::dialog(*gui_,"",msg,gui::OK_ONLY).show();
01394                 }
01395             }
01396         } else if(action.is_recruit()) {
01397             // Redo recruit action
01398             team& current_team = teams_[team_num-1];
01399             gamemap::location loc = action.recall_loc;
01400             const std::string name = action.affected_unit.type_id();
01401 
01402             //search for the unit to be recruited in recruits
01403             int recruit_num = 0;
01404             const std::set<std::string>& recruits = current_team.recruits();
01405             for(std::set<std::string>::const_iterator r = recruits.begin(); ; ++r) {
01406                 if (r == recruits.end()) {
01407                     ERR_NG << "trying to redo a recruit for side " << team_num
01408                         << ", which does not recruit type \"" << name << "\"\n";
01409                     assert(false);
01410                     return;
01411                 }
01412                 if (name == *r) {
01413                     break;
01414                 }
01415                 ++recruit_num;
01416             }
01417             last_recruit_ = name;
01418             recorder.add_recruit(recruit_num,loc);
01419             unit new_unit = action.affected_unit;
01420             //unit new_unit(action.affected_unit.type(),team_num_,true);
01421             const std::string& msg = recruit_unit(map_,team_num,units_,new_unit,loc,false,(gui_!=NULL));
01422             if(msg.empty()) {
01423                 current_team.spend_gold(new_unit.type()->cost());
01424                 statistics::recruit_unit(new_unit);
01425 
01426                 //MP_COUNTDOWN: restore recruitment bonus
01427                 current_team.set_action_bonus_count(1 + current_team.action_bonus_count());
01428 
01429                 gui_->invalidate(action.recall_loc);
01430                 gui_->draw();
01431                 //gui_.invalidate_game_status();
01432                 //gui_.invalidate_all();
01433                 recorder.add_checksum_check(loc);
01434             } else {
01435                 recorder.undo();
01436                 gui::dialog(*gui_,"",msg,gui::OK_ONLY).show();
01437             }
01438         } else {
01439             // Redo movement action
01440             const int starting_moves = action.starting_moves;
01441             std::vector<gamemap::location> route = action.route;
01442             const unit_map::iterator u = units_.find(route.front());
01443             if(u == units_.end()) {
01444                 assert(false);
01445                 return;
01446             }
01447 
01448             action.starting_moves = u->second.movement_left();
01449 
01450             unit_display::move_unit(route,u->second,teams_);
01451             std::pair<gamemap::location,unit> *up = units_.extract(u->first);
01452             up->second.set_goto(gamemap::location());
01453             up->second.set_movement(starting_moves);
01454             up->first = route.back();
01455             units_.add(up);
01456             unit::clear_status_caches();
01457             up->second.set_standing(up->first);
01458 
01459             if(map_.is_village(route.back())) {
01460                 get_village(route.back(),*gui_,teams_,up->second.side()-1,units_);
01461                 //MP_COUNTDOWN restore capture bonus
01462                 if(action.countdown_time_bonus)
01463                 {
01464                     teams_[team_num-1].set_action_bonus_count(1 + teams_[team_num-1].action_bonus_count());
01465                 }
01466             }
01467 
01468             gui_->invalidate(route.back());
01469             gui_->draw();
01470 
01471             recorder.add_movement(route.front(),route.back());
01472         }
01473         gui_->invalidate_unit();
01474         gui_->invalidate_game_status();
01475 
01476         undo_stack_.push_back(action);
01477         redo_stack_.pop_back();
01478     }
01479 
01480     bool menu_handler::clear_shroud(const unsigned int team_num)
01481     {
01482         bool cleared = teams_[team_num - 1].auto_shroud_updates() &&
01483 			::clear_shroud(*gui_,map_,units_,teams_,team_num-1);
01484         return cleared;
01485     }
01486 
01487     void menu_handler::clear_undo_stack(const unsigned int team_num)
01488     {
01489         if(teams_[team_num - 1].auto_shroud_updates() == false)
01490             apply_shroud_changes(undo_stack_,gui_,map_,units_,teams_,team_num-1);
01491         undo_stack_.clear();
01492     }
01493 
01494     // Highlights squares that an enemy could move to on their turn, showing how many can reach each square.
01495     void menu_handler::show_enemy_moves(bool ignore_units, const unsigned int team_num)
01496     {
01497         gui_->unhighlight_reach();
01498 
01499         // Compute enemy movement positions
01500         for(unit_map::iterator u = units_.begin(); u != units_.end(); ++u) {
01501             bool invisible = u->second.invisible(u->first, units_, teams_);
01502 
01503             if(teams_[team_num - 1].is_enemy(u->second.side()) && !gui_->fogged(u->first) && !u->second.incapacitated() && !invisible) {
01504                 const unit_movement_resetter move_reset(u->second);
01505                 const bool teleports = u->second.get_ability_bool("teleport",u->first);
01506                 const paths& path = paths(map_,units_,
01507                                           u->first,teams_,false,teleports,teams_[gui_->viewing_team()], 0,false, ignore_units);
01508 
01509                 gui_->highlight_another_reach(path);
01510             }
01511         }
01512     }
01513 
01514     void menu_handler::toggle_shroud_updates(const unsigned int team_num) {
01515         bool auto_shroud = teams_[team_num - 1].auto_shroud_updates();
01516         // If we're turning automatic shroud updates on, then commit all moves
01517         if(auto_shroud == false) update_shroud_now(team_num);
01518         teams_[team_num - 1].set_auto_shroud_updates(!auto_shroud);
01519     }
01520 
01521     void menu_handler::update_shroud_now(const unsigned int team_num)
01522     {
01523         clear_undo_stack(team_num);
01524     }
01525 
01526     bool menu_handler::end_turn(const unsigned int team_num)
01527     {
01528         bool unmoved_units = false, partmoved_units = false, some_units_have_moved = false;
01529         int units_alive = 0;
01530         for(unit_map::const_iterator un = units_.begin(); un != units_.end(); ++un) {
01531             if(un->second.side() == team_num) {
01532                 units_alive++;
01533                 if(unit_can_move(un->first,units_,map_,teams_)) {
01534                     if(!un->second.has_moved()) {
01535                         unmoved_units = true;
01536                     }
01537 
01538                     partmoved_units = true;
01539                 }
01540                 if(un->second.has_moved()) {
01541                     some_units_have_moved = true;
01542                 }
01543             }
01544         }
01545 
01546         //Ask for confirmation if the player hasn't made any moves (other than gotos).
01547         if (preferences::confirm_no_moves() && units_alive && !some_units_have_moved) {
01548             const int res = gui::dialog(*gui_,"",_("You have not started your turn yet. Do you really want to end your turn?"), gui::YES_NO).show();
01549             if(res != 0) {
01550                 return false;
01551             }
01552         }
01553 
01554         // Ask for confirmation if units still have movement left
01555         if(preferences::yellow_confirm() && partmoved_units) {
01556             const int res = gui::dialog(*gui_,"",_("Some units have movement left. Do you really want to end your turn?"),gui::YES_NO).show();
01557             if (res != 0) {
01558                 return false;
01559             }
01560         } else if (preferences::green_confirm() && unmoved_units) {
01561             const int res = gui::dialog(*gui_,"",_("Some units have movement left. Do you really want to end your turn?"),gui::YES_NO).show();
01562             if (res != 0) {
01563                 return false;
01564             }
01565         }
01566 
01567         return true;
01568     }
01569 
01570     void menu_handler::goto_leader(const unsigned int team_num)
01571     {
01572         const unit_map::const_iterator i = team_leader(team_num,units_);
01573         if(i != units_.end()) {
01574             clear_shroud(team_num);
01575             gui_->scroll_to_tile(i->first,game_display::WARP);
01576         }
01577     }
01578 
01579     void menu_handler::unit_description(mouse_handler& mousehandler)
01580     {
01581         const unit_map::const_iterator un = current_unit(mousehandler);
01582         if(un != units_.end()) {
01583             dialogs::show_unit_description(*gui_, un->second);
01584         }
01585     }
01586 
01587     void menu_handler::rename_unit(mouse_handler& mousehandler)
01588     {
01589         const unit_map::iterator un = current_unit(mousehandler);
01590         if(un == units_.end() || gui_->viewing_team()+1 != un->second.side())
01591             return;
01592         if(un->second.unrenamable())
01593             return;
01594 
01595         std::string name = un->second.name();
01596         const int res = gui::show_dialog(*gui_,NULL,_("Rename Unit"),"", gui::OK_CANCEL,NULL,NULL,"",&name);
01597         if(res == 0) {
01598             recorder.add_rename(name, un->first);
01599             un->second.rename(name);
01600             gui_->invalidate_unit();
01601         }
01602     }
01603 
01604     unit_map::iterator menu_handler::current_unit(mouse_handler& mousehandler)
01605     {
01606         unit_map::iterator res = find_visible_unit(units_, mousehandler.get_last_hex(),
01607             map_, teams_, teams_[gui_->viewing_team()]);
01608         if(res != units_.end()) {
01609             return res;
01610         } else {
01611             return find_visible_unit(units_, mousehandler.get_selected_hex(),
01612             map_, teams_, teams_[gui_->viewing_team()]);
01613         }
01614     }
01615 
01616     unit_map::const_iterator menu_handler::current_unit(const mouse_handler& mousehandler) const
01617     {
01618         unit_map::const_iterator res = find_visible_unit(units_, mousehandler.get_last_hex(),
01619             map_, teams_, teams_[gui_->viewing_team()]);
01620         if(res != units_.end()) {
01621             return res;
01622         } else {
01623             return find_visible_unit(units_, mousehandler.get_selected_hex(),
01624             map_, teams_, teams_[gui_->viewing_team()]);
01625         }
01626     }
01627 
01628     void menu_handler::create_unit(mouse_handler& mousehandler)
01629     {
01630         std::vector<std::string> options;
01631         std::vector<const unit_type*> unit_choices;
01632         const std::string heading = std::string(1,HEADING_PREFIX) +
01633                                     _("Race")      + COLUMN_SEPARATOR +
01634                                     _("Type");
01635         options.push_back(heading);
01636 
01637         for(unit_type_data::unit_type_map::const_iterator i = unit_type_data::types().begin(); i != unit_type_data::types().end(); ++i) {
01638             std::stringstream row;
01639 
01640             unit_type_data::types().find(i->first, unit_type::WITHOUT_ANIMATIONS);
01641 
01642             std::string race;
01643             const race_map::const_iterator race_it = unit_type_data::types().races().find(i->second.race());
01644             if (race_it != unit_type_data::types().races().end()) {
01645                 race = race_it->second.plural_name();
01646             }
01647             row << race << COLUMN_SEPARATOR;
01648             row << i->second.type_name() << COLUMN_SEPARATOR;
01649 
01650             options.push_back(row.str());
01651             unit_choices.push_back(&(i->second));
01652         }
01653 
01654         int choice = 0;
01655         {
01656             gui::menu::basic_sorter sorter;
01657             sorter.set_alpha_sort(0).set_alpha_sort(1);
01658 
01659             dialogs::unit_types_preview_pane unit_preview(*gui_, &map_, unit_choices);
01660             gui::dialog umenu(*gui_, _("Create Unit (Debug!)"), "", gui::OK_CANCEL);
01661             umenu.set_menu(options, &sorter);
01662             umenu.add_pane(&unit_preview);
01663             //sort by race then by type name
01664             umenu.get_menu().sort_by(1);
01665             umenu.get_menu().sort_by(0);
01666             umenu.get_menu().reset_selection();
01667             unit_preview.set_selection(umenu.get_menu().selection());
01668             choice = umenu.show();
01669         }
01670 
01671         if (size_t(choice) < unit_choices.size()) {
01672             units_.erase(mousehandler.get_last_hex());
01673 
01674             unit chosen(&units_,&map_,&status_,&teams_,unit_choices[choice],1,false);
01675             chosen.new_turn();
01676             units_.add(new std::pair<gamemap::location,unit>(mousehandler.get_last_hex(),chosen));
01677 
01678             gui_->invalidate(mousehandler.get_last_hex());
01679             gui_->invalidate_unit();
01680         }
01681     }
01682 
01683     void menu_handler::change_unit_side(mouse_handler& mousehandler)
01684     {
01685         const unit_map::iterator i = units_.find(mousehandler.get_last_hex());
01686         if(i == units_.end()) {
01687             return;
01688         }
01689 
01690         int side = i->second.side();
01691         ++side;
01692         if(side > team::nteams()) {
01693             side = 1;
01694         }
01695 
01696         i->second.set_side(side);
01697     }
01698 
01699     void menu_handler::label_terrain(mouse_handler& mousehandler, bool team_only)
01700     {
01701         if(map_.on_board(mousehandler.get_last_hex()) == false) {
01702             return;
01703         }
01704         gui::dialog d(*gui_, _("Place Label"), "", gui::OK_CANCEL);
01705         const terrain_label* old_label = gui_->labels().get_label(mousehandler.get_last_hex());
01706         if (old_label) {
01707             d.set_textbox(_("Label: "), old_label->text(), map_labels::get_max_chars());
01708             team_only = !old_label->team_name().empty();
01709         } else {
01710             d.set_textbox(_("Label: "), "", map_labels::get_max_chars());
01711         }
01712         d.add_option(_("Team only"), team_only, gui::dialog::BUTTON_CHECKBOX_LEFT);
01713 
01714         if(!d.show()) {
01715             std::string team_name;
01716             SDL_Color colour = font::LABEL_COLOUR;
01717             std::ostringstream last_team_id;
01718             last_team_id << gamemap::MAX_PLAYERS;
01719             std::map<std::string, color_range>::iterator gp = game_config::team_rgb_range.find(last_team_id.str());
01720 
01721             if (d.option_checked()) {
01722                 team_name = gui_->labels().team_name();
01723             } else {
01724                 colour = int_to_color(team::get_side_rgb(gui_->viewing_team()+1));
01725             }
01726             const terrain_label *res = gui_->labels().set_label(mousehandler.get_last_hex(), d.textbox_text(), team_name, colour);
01727             if (res)
01728                 recorder.add_label(res);
01729         }
01730     }
01731 
01732     void menu_handler::clear_labels()
01733     {
01734         if (gui_->team_valid()
01735            && !is_observer())
01736         {
01737             gui_->labels().clear(gui_->current_team_name());
01738             recorder.clear_labels(gui_->current_team_name());
01739         }
01740     }
01741 
01742     void menu_handler::continue_move(mouse_handler& mousehandler, const unsigned int team_num)
01743     {
01744         unit_map::iterator i = current_unit(mousehandler);
01745         if(i == units_.end() || i->second.move_interrupted() == false) {
01746             i = units_.find(mousehandler.get_selected_hex());
01747             if (i == units_.end() || i->second.move_interrupted() == false) return;
01748         }
01749         move_unit_to_loc(i,i->second.get_interrupted_move(),true, team_num, mousehandler);
01750     }
01751 
01752     void menu_handler::move_unit_to_loc(const unit_map::const_iterator& ui, const gamemap::location& target, bool continue_move, const unsigned int team_num, mouse_handler& mousehandler)
01753     {
01754         assert(ui != units_.end());
01755 
01756         paths::route route = mousehandler.get_route(ui, target, teams_[team_num - 1]);
01757 
01758         if(route.steps.empty())
01759             return;
01760 
01761         assert(route.steps.front() == ui->first);
01762 
01763         gui_->set_route(&route);
01764         move_unit(gui_,map_,units_,teams_,route.steps,&recorder,&undo_stack_,NULL,continue_move);
01765         gui_->invalidate_game_status();
01766     }
01767 
01768     void menu_handler::toggle_grid()
01769     {
01770         preferences::set_grid(!preferences::grid());
01771         gui_->invalidate_all();
01772     }
01773 
01774     void menu_handler::unit_hold_position(mouse_handler& mousehandler, const unsigned int team_num)
01775     {
01776         const unit_map::iterator un = units_.find(mousehandler.get_selected_hex());
01777         if(un != units_.end() && un->second.side() == team_num && un->second.movement_left() >= 0) {
01778             un->second.set_hold_position(!un->second.hold_position());
01779             gui_->invalidate(mousehandler.get_selected_hex());
01780 
01781             mousehandler.set_current_paths(paths());
01782             gui_->draw();
01783 
01784             if(un->second.hold_position()) {
01785                 un->second.set_user_end_turn(true);
01786                 mousehandler.cycle_units(false);
01787             }
01788         }
01789     }
01790 
01791     void menu_handler::end_unit_turn(mouse_handler& mousehandler, const unsigned int team_num)
01792     {
01793         const unit_map::iterator un = units_.find(mousehandler.get_selected_hex());
01794         if(un != units_.end() && un->second.side() == team_num && un->second.movement_left() >= 0) {
01795             un->second.set_user_end_turn(!un->second.user_end_turn());
01796             if(un->second.hold_position() && !un->second.user_end_turn()){
01797               un->second.set_hold_position(false);
01798             }
01799             gui_->invalidate(mousehandler.get_selected_hex());
01800 
01801             mousehandler.set_current_paths(paths());
01802             gui_->draw();
01803 
01804             if(un->second.user_end_turn()) {
01805                 mousehandler.cycle_units(false);
01806             }
01807         }
01808     }
01809 
01810     void menu_handler::search()
01811     {
01812         std::ostringstream msg;
01813         msg << _("Search");
01814         if(last_search_hit_.valid()) {
01815             msg << " [" << last_search_ << "]";
01816         }
01817         msg << ':';
01818         textbox_info_.show(gui::TEXTBOX_SEARCH,msg.str(), "", false, *gui_);
01819     }
01820 
01821     void menu_handler::do_speak(){
01822         //None of the two parameters really needs to be passed since the informations belong to members of the class.
01823         //But since it makes the called method more generic, it is done anyway.
01824         chat_handler::do_speak(textbox_info_.box()->text(),textbox_info_.check() != NULL ? textbox_info_.check()->checked() : false);
01825     }
01826 
01827     void menu_handler::add_chat_message(const time_t& time,
01828             const std::string& speaker, int side, const std::string& message,
01829             game_display::MESSAGE_TYPE type)
01830     {
01831         gui_->add_chat_message(time, speaker, side, message, type, false);
01832     }
01833 
01834     //simple command args parser, separated from command_handler for clarity.
01835     //a word begins with a nonspace
01836     //n-th arg is n-th word up to the next space
01837     //n-th data is n-th word up to the end
01838     //cmd is 0-th arg, begins at 0 always.
01839     class cmd_arg_parser
01840     {
01841         public:
01842             cmd_arg_parser()
01843             : str_(""), args_end(false)
01844             {
01845                 args.push_back(0);
01846             }
01847             explicit cmd_arg_parser(const std::string& str)
01848             : str_(str), args_end(false)
01849             {
01850                 args.push_back(0);
01851             }
01852             void parse(const std::string& str)
01853             {
01854                 str_ = str;
01855                 args.clear();
01856                 args.push_back(0);
01857                 args_end = false;
01858             }
01859 
01860             const std::string& get_str() const
01861             {
01862                 return str_;
01863             }
01864             std::string get_arg(unsigned n) const
01865             {
01866                 advance_to_arg(n);
01867                 if (n < args.size()) {
01868                     return std::string(str_, args[n], str_.find(' ', args[n]) - args[n]);
01869                 } else {
01870                     return "";
01871                 }
01872             }
01873             std::string get_data(unsigned n) const
01874             {
01875                 advance_to_arg(n);
01876                 if (n < args.size()) {
01877                     return std::string(str_, args[n]);
01878                 } else {
01879                     return "";
01880                 }
01881             }
01882             std::string get_cmd() const
01883             {
01884                 return get_arg(0);
01885             }
01886         private:
01887             cmd_arg_parser& operator=(const cmd_arg_parser&);
01888             cmd_arg_parser(const cmd_arg_parser&);
01889             void advance_to_arg(unsigned n) const
01890             {
01891                 while (n < args.size() && !args_end) {
01892                     size_t first_space = str_.find_first_of(' ', args.back());
01893                     size_t next_arg_begin = str_.find_first_not_of(' ', first_space);
01894                     if (next_arg_begin != std::string::npos) {
01895                         args.push_back(next_arg_begin);
01896                     } else {
01897                         args_end = true;
01898                     }
01899                 }
01900             }
01901             std::string str_;
01902             mutable std::vector<size_t> args;
01903             mutable bool args_end;
01904     };
01905 
01906     //A helper class template with a slim public interface
01907     //This represents a map of strings to void()-member-function-of-Worker-pointers
01908     //with all the common functionality like general help, command help and aliases
01909     //Usage (of a derived class): Derived(specific-arguments) d; d.dispatch(command);
01910     //Derived classes should override virtual functions where noted.
01911     //The template parameter currently must be the dervived class itself,
01912     //i.e. class X : public map_command_handler<X>
01913     //To add a new command in a derived class:
01914     //  * add a new private void function() to the derived class
01915     //  * add it to the function map in init_map there, setting flags like
01916     //    "D" for debug only (checking the flag is also done in the derived class)
01917     //  * remember to add some help and/or usage information in init_map()
01918     template <class Worker>
01919     class map_command_handler
01920     {
01921         public:
01922             typedef void (Worker::*command_handler)();
01923             struct command
01924             {
01925                 command_handler handler;
01926                 std::string help; //long help text
01927                 std::string usage; //only args info
01928                 std::string flags;
01929                 explicit command(command_handler h, const std::string help="",
01930                     const std::string& usage="", const std::string flags="")
01931                 : handler(h), help(help), usage(usage), flags(flags)
01932                 {
01933                 }
01934                 bool has_flag(const char f) const
01935                 {
01936                     return flags.find(f) != flags.npos;
01937                 }
01938                 command& add_flag(const char f)
01939                 {
01940                     flags += f;
01941                     return *this;
01942                 }
01943             };
01944             typedef std::map<std::string, command> command_map;
01945             typedef std::map<std::string, std::string> command_alias_map;
01946 
01947             map_command_handler() : cap_("")
01948             {
01949             }
01950 
01951             virtual ~map_command_handler() {}
01952 
01953             bool empty() const
01954             {
01955                 return command_map_.empty();
01956             }
01957             bool has_command(const std::string& cmd) const
01958             {
01959                 return get_command(cmd) != 0;
01960             }
01961             //actual work function
01962             void dispatch(std::string cmd)
01963             {
01964                 if (empty()) {
01965                     init_map_default();
01966                     init_map();
01967                 }
01968 
01969                 // We recursively resolve alias (100 max to avoid infinite recursion)
01970                 for (int i=0; i < 100; ++i) {
01971                     parse_cmd(cmd);
01972                     std::string actual_cmd = get_actual_cmd(get_cmd());
01973                     if (actual_cmd == get_cmd())
01974                         break;
01975                     std::string data = get_data(1);
01976                     // translate the command and add space + data if any
01977                     cmd = actual_cmd + (data.empty() ? "" : " ") + data;
01978                 }
01979 
01980                 if (get_cmd().empty()) {
01981                     return;
01982                 }
01983 
01984                 if (const command* c = get_command(get_cmd())) {
01985                     if (is_enabled(*c)) {
01986                         (static_cast<Worker*>(this)->*(c->handler))();
01987                     } else {
01988                         print(get_cmd(), _("This command is currently unavailable."));
01989                     }
01990                 } else if (help_on_unknown_) {
01991                     print("help", "Unknown command (" + get_cmd() + "), try " + cmd_prefix_ + "help "
01992                         "for a list of available commands.");
01993                 }
01994             }
01995         protected:
01996             void init_map_default()
01997             {
01998                 register_command("help", &map_command_handler<Worker>::help,
01999                     _("Available commands list and command-specific help. "
02000                     "Use \"help all\" to include currently unavailable commands."), "[all|<command>]");
02001             }
02002             //derived classes initialize the map overriding this function
02003             virtual void init_map() = 0;
02004             //overriden in derived classes to actually print the messages somwehere
02005             virtual void print(const std::string& title, const std::string& message) = 0;
02006             //should be overriden in derived classes if the commands have flags
02007             //this should return a string describing what all the flags mean
02008             virtual std::string get_flags_description() const
02009             {
02010                 return "";
02011             }
02012             //this should return a string describing the flags of the given command
02013             virtual std::string get_command_flags_description(const command& /*c*/) const
02014             {
02015                 return "";
02016             }
02017             //this should be overriden if e.g. flags are used to control command
02018             //availability. Return false if the command should not be executed by dispatch()
02019             virtual bool is_enabled(const command& /*c*/) const
02020             {
02021                 return true;
02022             }
02023             virtual void parse_cmd(const std::string& cmd_string)
02024             {
02025                 cap_.parse(cmd_string);
02026             }
02027             //safe n-th argunment getter
02028             virtual std::string get_arg(unsigned argn) const
02029             {
02030                 return cap_.get_arg(argn);
02031             }
02032             //"data" is n-th arg and everything after it
02033             virtual std::string get_data(unsigned argn = 1) const
02034             {
02035                 return cap_.get_data(argn);
02036             }
02037             std::string get_cmd() const
02038             {
02039                 return cap_.get_cmd();
02040             }
02041             //command error reporting shorthands
02042             void command_failed(const std::string& message)
02043             {
02044                 print(get_cmd(), "Error: " + message);
02045             }
02046             void command_failed_need_arg(int argn)
02047             {
02048                 command_failed("Missing argument " + lexical_cast<std::string>(argn));
02049             }
02050             void print_usage()
02051             {
02052                 help_command(get_cmd());
02053             }
02054             //take aliases into account
02055             std::string get_actual_cmd(const std::string& cmd) const
02056             {
02057                 command_alias_map::const_iterator i = command_alias_map_.find(cmd);
02058                 return i != command_alias_map_.end() ? i->second : cmd;
02059             }
02060             const command* get_command(const std::string& cmd) const
02061             {
02062                 typename command_map::const_iterator i = command_map_.find(cmd);
02063                 return i != command_map_.end() ? &i->second : 0;
02064             }
02065             command* get_command(const std::string& cmd)
02066             {
02067                 typename command_map::iterator i = command_map_.find(cmd);
02068                 return i != command_map_.end() ? &i->second : 0;
02069             }
02070             void help()
02071             {
02072                 //print command-specific help if available, otherwise list commands
02073                 if (help_command(get_arg(1))) {
02074                     return;
02075                 }
02076                 std::stringstream ss;
02077                 bool show_unavail = show_unavailable_ || get_arg(1) == "all";
02078                 BOOST_FOREACH(typename command_map::value_type i, command_map_) {
02079                     if (show_unavail || is_enabled(i.second)) {
02080                         ss << i.first;
02081                         //if (!i.second.usage.empty()) {
02082                         //  ss << " " << i.second.usage;
02083                         //}
02084                         //uncomment the above to display usage information in command list
02085                         //which might clutter it somewhat
02086                         if (!i.second.flags.empty()) {
02087                             ss << " (" << i.second.flags << ") ";
02088                         }
02089                         ss << "; ";
02090                     }
02091                 }
02092                 print("help", "Available commands " + get_flags_description() + ":\n" + ss.str());
02093                 print("help", "Type " + cmd_prefix_ + "help <command> for more info.");
02094             }
02095             //returns true if the command exists.
02096             bool help_command(const std::string& acmd)
02097             {
02098                 std::string cmd = get_actual_cmd(acmd);
02099                 const command* c = get_command(cmd);
02100                 if (c) {
02101                     std::stringstream ss;
02102                     ss << cmd_prefix_ << cmd;
02103                     if (c->help.empty() && c->usage.empty()) {
02104                         ss << _(" No help available.");
02105                     } else {
02106                         ss << " - " << c->help;
02107                     }
02108                     if (!c->usage.empty()) {
02109                         ss << " Usage: " << cmd_prefix_ << cmd << " " << c->usage;
02110                     }
02111                     ss << get_command_flags_description(*c);
02112                     const std::vector<std::string> l = get_aliases(cmd);
02113                     if (!l.empty()) {
02114                         ss << " (aliases: " << utils::join(l,' ') << ")";
02115                     }
02116                     print("help", ss.str());
02117                 }
02118                 return c != 0;
02119             }
02120             cmd_arg_parser cap_;
02121         protected:
02122             //show a "try help" message on unknown command?
02123             static void set_help_on_unknown(bool value)
02124             {
02125                 help_on_unknown_ = value;
02126             }
02127             //show all commands in help regardless whether they are active
02128             static void set_show_unavailable(bool value)
02129             {
02130                 show_unavailable_ = value;
02131             }           
02132             //this is display-only
02133             static void set_cmd_prefix(std::string value)
02134             {
02135                 cmd_prefix_ = value;
02136             }
02137             virtual void register_command(const std::string& cmd,
02138                 command_handler h, const std::string& help="",
02139                 const std::string& usage="", const std::string& flags="")
02140             {
02141                 command c = command(h, help, usage, flags);
02142                 std::pair<typename command_map::iterator, bool> r;
02143                 r = command_map_.insert(typename command_map::value_type(cmd, c));
02144                 if (!r.second) { //overwrite if exists
02145                     r.first->second = c;
02146                 }
02147             }
02148             virtual void assert_existence(const std::string& cmd) {
02149                 assert(command_map_.count(cmd));
02150             }
02151             virtual void register_alias(const std::string& to_cmd,
02152                 const std::string& cmd)
02153             {
02154                 // disable the assert to allow alias to "command + args"
02155                 // the fonction assert_existence seems unused now
02156                 //assert_existence(to_cmd);
02157                 command_alias_map_[cmd] = to_cmd;
02158             }
02159             //get all aliases of a command.
02160             static const std::vector<std::string> get_aliases(const std::string& cmd)
02161             {
02162                 std::vector<std::string> aliases;
02163                 typedef command_alias_map::value_type p;
02164                 BOOST_FOREACH(p i, command_alias_map_) {
02165                     if (i.second == cmd) {
02166                         aliases.push_back(i.first);
02167                     }
02168                 }
02169                 return aliases;
02170             }
02171         private:
02172             static command_map command_map_;
02173             static command_alias_map command_alias_map_;
02174             static bool help_on_unknown_;
02175             static bool show_unavailable_;
02176             static std::string cmd_prefix_;
02177     };
02178 
02179     //static member definitions
02180     template <class Worker>
02181     typename map_command_handler<Worker>::command_map map_command_handler<Worker>::command_map_;
02182 
02183     template <class Worker>
02184     typename map_command_handler<Worker>::command_alias_map map_command_handler<Worker>::command_alias_map_;
02185 
02186     template <class Worker>
02187     bool map_command_handler<Worker>::help_on_unknown_ = true;
02188 
02189     template <class Worker>
02190     bool map_command_handler<Worker>::show_unavailable_ = false;
02191 
02192     template <class Worker>
02193     std::string map_command_handler<Worker>::cmd_prefix_;
02194 
02195     //command handler for chat /commands
02196     class chat_command_handler : public map_command_handler<chat_command_handler>
02197     {
02198         public:
02199             typedef map_command_handler<chat_command_handler> map;
02200             chat_command_handler(chat_handler& chathandler, bool allies_only)
02201             : map(), chat_handler_(chathandler), allies_only_(allies_only)
02202             {
02203             }
02204 
02205         protected:
02206             void do_emote();
02207             void do_network_send();
02208             void do_whisper();
02209             void do_log();
02210             void do_ignore();
02211             void do_friend();
02212             void do_remove();
02213             void do_display();
02214             void do_version();
02215 
02216             void print(const std::string& title, const std::string& message)
02217             {
02218                 chat_handler_.add_chat_message(time(NULL), title, 0, message);
02219             }
02220             void init_map()
02221             {
02222                 set_cmd_prefix("/");
02223                 register_command("query", &chat_command_handler::do_network_send,
02224                     _("Send a query to the server. Without arguments the server"
02225                     " should tell you the available commands."));
02226                 register_command("ban", &chat_command_handler::do_network_send,
02227                     _("Ban and kick a player or observer. If he is not in the"
02228                     " game but on the server he will only be banned."), "<nick>");
02229                 register_command("kick", &chat_command_handler::do_network_send,
02230                     _("Kick a player or observer."), "<nick>");
02231                 register_command("mute", &chat_command_handler::do_network_send,
02232                     _("Mute an observer."), "<nick>");
02233                 register_command("muteall", &chat_command_handler::do_network_send,
02234                     _("Mute all observers."), "");
02235                 register_command("ping", &chat_command_handler::do_network_send,
02236                     "");
02237                 register_command("emote", &chat_command_handler::do_emote,
02238                     _("Send an emotion or personal action in chat."), "<message>");
02239                 register_alias("emote", "me");
02240                 register_command("whisper", &chat_command_handler::do_whisper,
02241                     _("Sends a private message. "
02242                     "You can't send messages to players that control "
02243                     "a side in a running game you are in."), "<nick> <message>");
02244                 register_alias("whisper", "msg");
02245                 register_alias("whisper", "m");
02246                 register_command("log", &chat_command_handler::do_log,
02247                     _("Change the log level of a log domain."), "<level> <domain>");
02248                 register_command("ignore", &chat_command_handler::do_ignore,
02249                     _("Add a nick to your ignores list."), "<nick>");
02250                 register_command("friend", &chat_command_handler::do_friend,
02251                     _("Add a nick to your friends list."), "<nick>");
02252                 register_command("remove", &chat_command_handler::do_remove,
02253                     _("Remove a nick from your ignores or friends list."), "<nick>");
02254                 register_command("list", &chat_command_handler::do_display,
02255                     _("Show your ignores and friends list."));
02256                 register_alias("list", "display");
02257                 register_command("version", &chat_command_handler::do_version,
02258                     _("Display version information."));
02259             }
02260         private:
02261             chat_handler& chat_handler_;
02262             bool allies_only_;
02263     };
02264 
02265     //command handler for user :commands. Also understands all chat commands
02266     //via inheritance. This complicates some things a bit.
02267     class console_handler : public map_command_handler<console_handler>, private chat_command_handler
02268     {
02269         public:
02270             //convenience typedef
02271             typedef map_command_handler<console_handler> chmap;
02272             console_handler(menu_handler& menu_handler,
02273                 mouse_handler& mouse_handler, const unsigned int team_num)
02274             : chmap(), chat_command_handler(menu_handler, true), menu_handler_(menu_handler), mouse_handler_(mouse_handler)
02275                 , team_num_(team_num)
02276             {
02277             }
02278             using chmap::dispatch; //disambiguate
02279 
02280         protected:
02281             //chat_command_handler's init_map() and hanlers will end up calling these.
02282             //this makes sure the commands end up in our map
02283             virtual void register_command(const std::string& cmd,
02284                 chat_command_handler::command_handler h, const std::string& help="",
02285                 const std::string& usage="", const std::string& flags="")
02286             {
02287                 chmap::register_command(cmd, h, help, usage, flags + "N"); //add chat commands as network_only
02288             }
02289             virtual void assert_existence(const std::string& cmd) {
02290                 chmap::assert_existence(cmd);
02291             }
02292             virtual void register_alias(const std::string& to_cmd,
02293                 const std::string& cmd)
02294             {
02295                 chmap::register_alias(to_cmd, cmd);
02296             }
02297             virtual std::string get_arg(unsigned i) const
02298             {
02299                 return chmap::get_arg(i);
02300             }
02301             virtual std::string get_cmd() const
02302             {
02303                 return chmap::get_cmd();
02304             }
02305             virtual std::string get_data(unsigned n = 1) const
02306             {
02307                 return chmap::get_data(n);
02308             }
02309 
02310             //these are needed to avoid ambiguities introduced by inheriting from console_command_handler
02311             using chmap::register_command;
02312             using chmap::register_alias;
02313             using chmap::help;
02314             using chmap::is_enabled;
02315             using chmap::command_failed;
02316             using chmap::command_failed_need_arg;
02317 
02318             void do_refresh();
02319             void do_droid();
02320             void do_theme();
02321             void do_control();
02322             void do_clear();
02323             void do_sunset();
02324             void do_fps();
02325             void do_benchmark();
02326             void do_save();
02327             void do_save_quit();
02328             void do_quit();
02329             void do_ignore_replay_errors();
02330             void do_nosaves();
02331             void do_next_level();
02332             void do_debug();
02333             void do_nodebug();
02334             void do_custom();
02335             void do_set_alias();
02336             void do_set_var();
02337             void do_show_var();
02338             void do_unit();
02339             void do_buff();
02340             void do_unbuff();
02341             void do_create();
02342             void do_fog();
02343             void do_shroud();
02344             void do_gold();
02345             void do_event();
02346 
02347             std::string get_flags_description() const {
02348                 return "(D) - debug only, (N) - network only";
02349             }
02350             using chat_command_handler::get_command_flags_description; //silence a warning
02351             std::string get_command_flags_description(const chmap::command& c) const
02352             {
02353                 return std::string(c.has_flag('D') ? " (debug command)" : "")
02354                      + std::string(c.has_flag('N') ? " (network only)" : "");
02355             }
02356             using map::is_enabled;
02357             bool is_enabled(const chmap::command& c) const
02358             {
02359                 return !((c.has_flag('D') && !game_config::debug)
02360                       || (c.has_flag('N') && network::nconnections() == 0));
02361             }
02362             void print(const std::string& title, const std::string& message)
02363             {
02364                 menu_handler_.add_chat_message(time(NULL), title, 0, message);
02365             }
02366             void init_map()
02367             {
02368                 chat_command_handler::init_map();//grab chat_ /command handlers
02369                 chmap::get_command("log")->flags = ""; //clear network-only flag from log
02370                 chmap::get_command("version")->flags = ""; //clear network-only flag
02371                 chmap::get_command("ignore")->flags = ""; //clear network-only flag
02372                 chmap::get_command("friend")->flags = ""; //clear network-only flag
02373                 chmap::get_command("list")->flags = ""; //clear network-only flag
02374                 chmap::get_command("remove")->flags = ""; //clear network-only flag
02375                 chmap::set_cmd_prefix(":");
02376                 register_command("refresh", &console_handler::do_refresh,
02377                     _("Refresh gui."));
02378                 register_command("droid", &console_handler::do_droid,
02379                     _("Switch a side to/from AI control."), "[<side> [on/off]]");
02380                 register_command("theme", &console_handler::do_theme);
02381                 register_command("control", &console_handler::do_control,
02382                     _("Assign control of a side to a different player or observer."), "<side> <nick>", "N");
02383                 register_command("clear", &console_handler::do_clear,
02384                     _("Clear chat history."));
02385                 register_command("sunset", &console_handler::do_sunset,
02386                     _("Visualize the screen refresh procedure."), "", "D");
02387                 register_command("fps", &console_handler::do_fps, "Show fps.");
02388                 register_command("benchmark", &console_handler::do_benchmark);
02389                 register_command("save", &console_handler::do_save, _("Save game."));
02390                 register_alias("save", "w");
02391                 register_command("quit", &console_handler::do_quit, _("Quit game."));
02392                 register_alias("quit", "q");
02393                 register_alias("quit", "q!");
02394                 register_command("save_quit", &console_handler::do_save_quit,
02395                     _("Save and quit."));
02396                 register_alias("save_quit", "wq");
02397                 register_command("ignore_replay_errors", &console_handler::do_ignore_replay_errors,
02398                     _("Ignore replay errors."));
02399                 register_command("nosaves", &console_handler::do_nosaves,
02400                     _("Disable autosaves."));
02401                 register_command("next_level", &console_handler::do_next_level,
02402                     _("Advance to the next scenario."), "", "D");
02403                 register_alias("next_level", "n");
02404                 register_command("debug", &console_handler::do_debug,
02405                     _("Turn debug mode on."));
02406                 register_command("nodebug", &console_handler::do_nodebug,
02407                     _("Turn debug mode off."), "", "D");
02408                 register_command("custom", &console_handler::do_custom,
02409                     _("Set the command used by the custom command hotkey"), "<command>");
02410                 register_command("alias", &console_handler::do_set_alias,
02411                     _("Set a alias to a command"), "<name>=<command>");
02412                 register_command("set_var", &console_handler::do_set_var,
02413                     _("Set a scenario variable."), "<var>=<value>", "D");
02414                 register_command("show_var", &console_handler::do_show_var,
02415                     _("Show a scenario variable."), "<var>", "D");
02416                 register_command("unit", &console_handler::do_unit,
02417                     _("Modify a unit variable. (Only top level keys are supported.)"), "", "D");
02418                 register_command("buff", &console_handler::do_buff,
02419                     _("Add a trait to a unit."), "", "D");
02420                 register_command("unbuff", &console_handler::do_unbuff,
02421                     _("Remove a trait from a unit. (Does not work yet.)"), "", "D");
02422                 register_command("create", &console_handler::do_create,
02423                     _("Create a unit."), "", "D");
02424                 register_command("fog", &console_handler::do_fog,
02425                     _("Toggle fog for the current player."), "", "D");
02426                 register_command("shroud", &console_handler::do_shroud,
02427                     _("Toggle shroud for the current player."), "", "D");
02428                 register_command("gold", &console_handler::do_gold,
02429                     _("Give gold to the current player."), "", "D");
02430                 register_command("throw", &console_handler::do_event,
02431                     _("Fire a game event."), "", "D");
02432                 register_alias("throw", "fire");
02433             }
02434         private:
02435             menu_handler& menu_handler_;
02436             mouse_handler& mouse_handler_;
02437             const unsigned int team_num_;
02438     };
02439 
02440     chat_handler::chat_handler()
02441     {
02442     }
02443 
02444     chat_handler::~chat_handler()
02445     {
02446     }
02447 
02448     //! Change the log level of a log domain.
02449     //! @param data String of the form: '<level> <domain>'
02450     void chat_handler::change_logging(const std::string& data) {
02451         const std::string::const_iterator j =
02452                 std::find(data.begin(), data.end(), ' ');
02453         if (j == data.end()) return;
02454         const std::string level(data.begin(),j);
02455         const std::string domain(j+1,data.end());
02456         int severity;
02457         if (level == "error") severity = 0;
02458         else if (level == "warning") severity = 1;
02459         else if (level == "info") severity = 2;
02460         else if (level == "debug") severity = 3;
02461         else {
02462             utils::string_map symbols;
02463             symbols["level"] = level;
02464             const std::string& msg =
02465                     vgettext("Unknown debug level: '$level'.", symbols);
02466             ERR_NG << msg << "\n";
02467             add_chat_message(time(NULL), _("error"), 0, msg);
02468             return;
02469         }
02470         if (!lg::set_log_domain_severity(domain, severity)) {
02471             utils::string_map symbols;
02472             symbols["domain"] = domain;
02473             const std::string& msg =
02474                     vgettext("Unknown debug domain: '$domain'.", symbols);
02475             ERR_NG << msg << "\n";
02476             add_chat_message(time(NULL), _("error"), 0, msg);
02477             return;
02478         } else {
02479             utils::string_map symbols;
02480             symbols["level"] = level;
02481             symbols["domain"] = domain;
02482             const std::string& msg =
02483                     vgettext("Switched domain: '$domain' to level: '$level'.", symbols);
02484             LOG_NG << msg << "\n";
02485             add_chat_message(time(NULL), "log", 0, msg);
02486         }
02487     }
02488 
02489     void chat_handler::send_command(const std::string& cmd, const std::string& args /* = "" */) {
02490         config data;
02491         if (cmd == "muteall") {
02492             data.add_child(cmd);
02493         } else if (cmd == "query") {
02494             data.add_child(cmd)["type"] = args;
02495         } else if (cmd == "ban" || cmd == "kick" || cmd == "mute") {
02496             data.add_child(cmd)["username"] = args;
02497         } else if (cmd == "ping") {
02498             data[cmd] = lexical_cast<std::string>(time(NULL));
02499         }
02500         network::send_data(data, 0, true);
02501     }
02502 
02503     void chat_handler::do_speak(const std::string& message, bool allies_only)
02504     {
02505         if(message == "") {
02506             return;
02507         }
02508         bool is_command = (message.at(0) == '/');
02509 
02510         if(!is_command) {
02511             send_chat_message(message, allies_only);
02512             return;
02513         }
02514         std::string cmd(message.begin() + 1, message.end());
02515         chat_command_handler cch(*this, allies_only);
02516         cch.dispatch(cmd);
02517     }
02518     void chat_command_handler::do_emote()
02519     {
02520         chat_handler_.send_chat_message("/me " + get_data(), allies_only_);
02521     }
02522     void chat_command_handler::do_network_send()
02523     {
02524         chat_handler_.send_command(get_cmd(), get_data());
02525     }
02526     void chat_command_handler::do_whisper()
02527     {
02528         if (get_data(1).empty()) return command_failed_need_arg(1);
02529         if (get_data(2).empty()) return command_failed_need_arg(2);
02530         config cwhisper, data;
02531         cwhisper["receiver"] = get_arg(1);
02532         cwhisper["message"] = get_data(2);
02533         cwhisper["sender"] = preferences::login();
02534         data.add_child("whisper", cwhisper);
02535         chat_handler_.add_chat_message(time(NULL),
02536             "whisper to " + cwhisper["receiver"], 0,
02537             cwhisper["message"], game_display::MESSAGE_PRIVATE);
02538         network::send_data(data, 0, true);
02539     }
02540     void chat_command_handler::do_log()
02541     {
02542         chat_handler_.change_logging(get_data());
02543     }
02544 
02545     void chat_command_handler::do_ignore()
02546     {
02547         if (get_arg(1).empty()) {
02548             const std::string& tmp = preferences::get_ignores();
02549             print("ignores list", tmp.empty() ? "(empty)" : tmp);
02550         } else {
02551             if (preferences::add_ignore(get_arg(1))) {
02552                 print("ignore",  _("Added to ignore list: ") + get_arg(1));
02553             } else {
02554                 command_failed(_("Invalid username: ") + get_arg(1));
02555             }
02556         }
02557     }
02558     void chat_command_handler::do_friend()
02559     {
02560         if (get_arg(1).empty()) {
02561             const std::string& tmp = preferences::get_friends();
02562             print("friends list", tmp.empty() ? "(empty)" : tmp);
02563         } else {
02564             if (preferences::add_friend(get_arg(1))) {
02565                 print("friend",  _("Added to friends list: ") + get_arg(1));
02566             } else {
02567                 command_failed(_("Invalid username: ") + get_arg(1));
02568             }
02569         }
02570     }
02571     void chat_command_handler::do_remove()
02572     {
02573         preferences::remove_friend(get_arg(1));
02574         preferences::remove_ignore(get_arg(1));
02575         print("list", _("Removed from list: ") + get_arg(1));
02576     }
02577     void chat_command_handler::do_display()
02578     {
02579         const std::string& text_friend = preferences::get_friends();
02580         const std::string& text_ignore = preferences::get_ignores();
02581         if (!text_friend.empty()) {
02582             print("friends list", text_friend);
02583         }
02584         if (!text_ignore.empty()) {
02585             print("ignores list", text_ignore);
02586         } else if (text_friend.empty()) {
02587             print("list", _("There are no players on your friends or ignore list."));
02588         }
02589     }
02590     void chat_command_handler::do_version() {
02591         print("version", game_config::revision);
02592     }
02593 
02594     void menu_handler::send_chat_message(const std::string& message, bool allies_only)
02595     {
02596         config cfg;
02597         cfg["id"] = preferences::login();
02598         cfg["message"] = message;
02599 
02600         const int side = is_observer() ? 0 : gui_->viewing_team()+1;
02601         if(!is_observer()) {
02602             cfg["side"] = lexical_cast<std::string>(side);
02603         }
02604 
02605         bool private_message = has_friends() && allies_only;
02606 
02607         if(private_message) {
02608             if (is_observer()) {
02609                 cfg["team_name"] = game_config::observer_team_name;
02610             } else {
02611                 cfg["team_name"] = teams_[gui_->viewing_team()].team_name();
02612             }
02613         }
02614 
02615         recorder.speak(cfg);
02616         add_chat_message(time(NULL), cfg["id"], side, message,
02617                 private_message ? game_display::MESSAGE_PRIVATE : game_display::MESSAGE_PUBLIC);
02618 
02619     }
02620 
02621 
02622     void menu_handler::do_search(const std::string& new_search)
02623     {
02624         if(new_search.empty() == false && new_search != last_search_)
02625             last_search_ = new_search;
02626 
02627         if(last_search_.empty()) return;
02628 
02629         bool found = false;
02630         gamemap::location loc = last_search_hit_;
02631         //If this is a location search, just center on that location.
02632         std::vector<std::string> args = utils::split(last_search_, ',');
02633         if(args.size() == 2) {
02634             int x, y;
02635             x = lexical_cast_default<int>(args[0], 0)-1;
02636             y = lexical_cast_default<int>(args[1], 0)-1;
02637             if(x >= 0 && x < map_.w() && y >= 0 && y < map_.h()) {
02638                 loc = gamemap::location(x,y);
02639                 found = true;
02640             }
02641         }
02642         //Start scanning the game map
02643         if(loc.valid() == false)
02644             loc = gamemap::location(map_.w()-1,map_.h()-1);
02645         gamemap::location start = loc;
02646         while (!found) {
02647             //Move to the next location
02648             loc.x = (loc.x + 1) % map_.w();
02649             if(loc.x == 0)
02650                 loc.y = (loc.y + 1) % map_.h();
02651 
02652             //Search label
02653             if (!gui_->shrouded(loc)) {
02654                 const terrain_label* label = gui_->labels().get_label(loc);
02655                 if(label) {
02656                     if(std::search(label->text().begin(), label->text().end(),
02657                             last_search_.begin(), last_search_.end(),
02658                             chars_equal_insensitive) != label->text().end()) {
02659                         found = true;
02660                     }
02661                 }
02662             }
02663             //Search unit name
02664             if (!gui_->fogged(loc)) {
02665                 unit_map::const_iterator ui = units_.find(loc);
02666                 if(ui != units_.end()) {
02667                     const std::string name = ui->second.name();
02668                     if(std::search(name.begin(), name.end(),
02669                             last_search_.begin(), last_search_.end(),
02670                             chars_equal_insensitive) != name.end()) {
02671                         if (!teams_[gui_->viewing_team()].is_enemy(ui->second.side())
02672                             || !ui->second.invisible(ui->first, units_,teams_)) {
02673                             found = true;
02674                         }
02675                     }
02676                 }
02677             }
02678 
02679             if(loc == start)
02680                 break;
02681         }
02682 
02683         if(found) {
02684             last_search_hit_ = loc;
02685             gui_->scroll_to_tile(loc,game_display::ONSCREEN,false);
02686             gui_->highlight_hex(loc);
02687         } else {
02688             last_search_hit_ = gamemap::location();
02689             //Not found, inform the player
02690             utils::string_map symbols;
02691             symbols["search"] = last_search_;
02692             const std::string msg = vgettext("Couldn't find label or unit "
02693                     "containing the string '$search'.", symbols);
02694             gui::dialog(*gui_,"",msg).show();
02695         }
02696     }
02697 
02698     void menu_handler::do_command(const std::string& str,
02699             const unsigned int team_num, mouse_handler& mousehandler)
02700     {
02701         console_handler ch(*this, mousehandler, team_num);
02702         ch.dispatch(str);
02703     }
02704 
02705     void console_handler::do_refresh() {
02706         image::flush_cache();
02707         menu_handler_.gui_->redraw_everything();
02708     }
02709 
02710     void console_handler::do_droid() {
02711         // :droid [<side> [on/off]]
02712         const std::string side_s = get_arg(1);
02713         const std::string action = get_arg(2);
02714         // default to the current side if empty
02715         const unsigned int side = side_s.empty() ?
02716             team_num_ : lexical_cast_default<unsigned int>(side_s);
02717 
02718         if (side < 1 || side > menu_handler_.teams_.size()) {
02719             utils::string_map symbols;
02720             symbols["side"] = side_s;
02721             command_failed(vgettext("Can't droid invalid side: '$side'.", symbols));
02722             return;
02723         } else if (menu_handler_.teams_[side - 1].is_network()) {
02724             utils::string_map symbols;
02725             symbols["side"] = lexical_cast<std::string>(side);
02726             command_failed(vgettext("Can't droid networked side: '$side'.", symbols));
02727             return;
02728         } else if (menu_handler_.teams_[side - 1].is_human() && action != " off") {
02729             //this is our side, so give it to AI
02730             menu_handler_.teams_[side - 1].make_ai();
02731             menu_handler_.textbox_info_.close(*menu_handler_.gui_);
02732             if(team_num_ == side) {
02733                 //if it is our turn at the moment, we have to indicate to the
02734                 //play_controller, that we are no longer in control
02735                 throw end_turn_exception(side);
02736             }
02737         } else if (menu_handler_.teams_[side - 1].is_ai() && action != " on") {
02738             menu_handler_.teams_[side - 1].make_human();
02739         }
02740     }
02741     void console_handler::do_theme() {
02742         preferences::show_theme_dialog(*menu_handler_.gui_);
02743     }
02744     void console_handler::do_control() {
02745         // :control <side> <nick>
02746         if (network::nconnections() == 0) return;
02747         const std::string side = get_arg(1);
02748         const std::string player = get_arg(2);
02749         if(player.empty())
02750         {
02751             command_failed_need_arg(2);
02752             return;
02753         }
02754 
02755         unsigned int side_num;
02756         try {
02757             side_num = lexical_cast<unsigned int>(side);
02758         } catch(bad_lexical_cast&) {
02759             utils::string_map symbols;
02760             symbols["side"] = side;
02761             command_failed(vgettext("Can't change control of invalid side: '$side'.", symbols));
02762             return;
02763         }
02764         if (side_num < 1 || side_num > menu_handler_.teams_.size()) {
02765             utils::string_map symbols;
02766             symbols["side"] = side;
02767             command_failed(vgettext("Can't change control of out-of-bounds side: '$side'.", symbols));
02768             return;
02769         }
02770         //if this is our side we are always allowed to change the controller
02771         if(menu_handler_.teams_[side_num - 1].is_human()){
02772             if (player == preferences::login())
02773                 return;
02774             menu_handler_.change_side_controller(side,player,true);
02775             menu_handler_.textbox_info_.close(*(menu_handler_.gui_));
02776         } else {
02777             //it is not our side, the server will decide if we can change the
02778             //controller (that is if we are host of the game)
02779             menu_handler_.change_side_controller(side,player);
02780         }
02781     }
02782     void console_handler::do_clear() {
02783         menu_handler_.gui_->clear_chat_messages();
02784     }
02785     void console_handler::do_sunset() {
02786         int delay = lexical_cast_default<int>(get_data());
02787         menu_handler_.gui_->sunset(delay);
02788     }
02789     void console_handler::do_fps() {
02790         preferences::set_show_fps(!preferences::show_fps());
02791     }
02792     void console_handler::do_benchmark() {
02793         menu_handler_.gui_->toggle_benchmark();
02794     }
02795     void console_handler::do_save() {
02796         menu_handler_.save_game(get_data(),gui::NULL_DIALOG);
02797     }
02798     void console_handler::do_save_quit() {
02799         menu_handler_.save_game(get_data(),gui::NULL_DIALOG);
02800         throw end_level_exception(QUIT);
02801     }
02802     void console_handler::do_quit() {
02803         throw end_level_exception(QUIT);
02804     }
02805     void console_handler::do_ignore_replay_errors() {
02806         game_config::ignore_replay_errors = (get_data() != "off") ? true : false;
02807     }
02808     void console_handler::do_nosaves() {
02809         game_config::disable_autosave = (get_data() != "off") ? true : false;
02810     }
02811     void console_handler::do_next_level() {
02812         throw end_level_exception(LEVEL_CONTINUE_NO_SAVE);
02813     }
02814     void console_handler::do_debug() {
02815         if (network::nconnections() == 0) {
02816             print(get_cmd(), _("Debug mode activated!"));
02817             game_config::debug = true;
02818         } else {
02819             command_failed(_("Debug mode not available in network games"));
02820         }
02821     }
02822     void console_handler::do_nodebug() {
02823         if (game_config::debug) {
02824             print(get_cmd(), _("Debug mode deactivated!"));
02825             game_config::debug = false;
02826         }
02827     }
02828     void console_handler::do_custom() {
02829         preferences::set("custom_command", get_data());
02830     }
02831     void console_handler::do_set_alias() {
02832         const std::string data = get_data();
02833         const std::string::const_iterator j = std::find(data.begin(),data.end(),'=');
02834         if(j != data.end()) {
02835             const std::string alias(data.begin(),j);
02836             const std::string command(j+1,data.end());
02837             register_alias(command, alias);
02838         }
02839     }
02840     void console_handler::do_set_var() {
02841         const std::string data = get_data();
02842         if (data.empty()) {
02843             command_failed_need_arg(1);
02844             return;
02845         }
02846         const std::string::const_iterator j = std::find(data.begin(),data.end(),'=');
02847         if(j != data.end()) {
02848             const std::string name(data.begin(),j);
02849             const std::string value(j+1,data.end());
02850             menu_handler_.gamestate_.set_variable(name,value);
02851         } else {
02852             command_failed("Variable not found");
02853         }
02854     }
02855     void console_handler::do_show_var() {
02856         gui::message_dialog to_show(*menu_handler_.gui_,"",menu_handler_.gamestate_.get_variable(get_data()));
02857         to_show.show();
02858     }
02859     void console_handler::do_unit() {
02860         // prevent SIGSEGV due to attempt to set HP during a fight
02861         if (events::commands_disabled > 0)
02862             return;
02863         const unit_map::iterator i = menu_handler_.current_unit(mouse_handler_);
02864         if (i == menu_handler_.units_.end()) return;
02865         const std::string name = get_arg(1);
02866         const std::string value = get_data(2);
02867         if (value.empty()) return;
02868         // FIXME: Avoids a core dump on display
02869         // because alignment strings get reduced
02870         // to an enum, then used to index an
02871         // array of strings.
02872         // But someday the code ought to be
02873         // changed to allow general string
02874         // alignments for UMC.
02875         if (name == "alignment" && (value != "lawful" && value != "neutral" && value != "chaotic")) {
02876             command_failed("Invalid alignment: '" + value + "', needs to be one of lawful, neutral or chaotic.");
02877             return;
02878         }
02879         config cfg;
02880         i->second.write(cfg);
02881         cfg[name] = value;
02882         i->second = unit(&menu_handler_.units_,&menu_handler_.map_,&menu_handler_.status_,&menu_handler_.teams_,cfg);
02883         menu_handler_.gui_->invalidate(i->first);
02884         menu_handler_.gui_->invalidate_unit();
02885     }
02886     void console_handler::do_buff() {
02887         const unit_map::iterator i = menu_handler_.current_unit(mouse_handler_);
02888         if(i != menu_handler_.units_.end()) {
02889             i->second.add_trait(get_data());
02890             menu_handler_.gui_->invalidate(i->first);
02891             menu_handler_.gui_->invalidate_unit();
02892         } else {
02893             command_failed("No unit selected");
02894         }
02895     }
02896     void console_handler::do_unbuff() {
02897         const unit_map::iterator i = menu_handler_.current_unit(mouse_handler_);
02898         if(i != menu_handler_.units_.end()) {
02899             // FIXME: 'data_' is the trait.  Clear it.
02900 
02901             menu_handler_.gui_->invalidate(i->first);
02902             menu_handler_.gui_->invalidate_unit();
02903         } else {
02904             command_failed("No unit selected");
02905         }
02906     }
02907     void console_handler::do_create() {
02908         if (menu_handler_.map_.on_board(mouse_handler_.get_last_hex())) {
02909             const unit_type_data::unit_type_map::const_iterator i = unit_type_data::types().find(get_data());
02910             if(i == unit_type_data::types().end()) {
02911                 command_failed("Invalid unit type");
02912                 return;
02913             }
02914 
02915             menu_handler_.units_.erase(mouse_handler_.get_last_hex());
02916             menu_handler_.units_.add(new std::pair<gamemap::location,unit>(
02917                 mouse_handler_.get_last_hex(),
02918                 unit(&menu_handler_.units_,&menu_handler_.map_,&menu_handler_.status_,&menu_handler_.teams_,&i->second,1,false)));
02919             menu_handler_.gui_->invalidate(mouse_handler_.get_last_hex());
02920             menu_handler_.gui_->invalidate_unit();
02921         } else {
02922             command_failed("Invalid location");
02923         }
02924     }
02925     void console_handler::do_fog() {
02926         menu_handler_.teams_[team_num_ - 1].set_fog( !menu_handler_.teams_[team_num_ - 1].uses_fog() );
02927         recalculate_fog(menu_handler_.map_,menu_handler_.units_,menu_handler_.teams_, team_num_ - 1);
02928         menu_handler_.gui_->recalculate_minimap();
02929         menu_handler_.gui_->redraw_everything();
02930     }
02931     void console_handler::do_shroud() {
02932         menu_handler_.teams_[team_num_ - 1].set_shroud( !menu_handler_.teams_[team_num_ - 1].uses_shroud() );
02933         menu_handler_.clear_shroud(team_num_);
02934         menu_handler_.gui_->recalculate_minimap();
02935         menu_handler_.gui_->redraw_everything();
02936     }
02937     void console_handler::do_gold() {
02938         menu_handler_.teams_[team_num_ - 1].spend_gold(-lexical_cast_default<int>(get_data(),1000));
02939         menu_handler_.gui_->redraw_everything();
02940     }
02941     void console_handler::do_event() {
02942         game_events::fire(get_data());
02943         menu_handler_.gui_->redraw_everything();
02944     }
02945 
02946     void menu_handler::do_ai_formula(const std::string& str,
02947             const unsigned int team_num, mouse_handler& /*mousehandler*/)
02948     {
02949         replay dummy_replay;
02950         replay_network_sender dummy_sender(dummy_replay);
02951         undo_list dummy_undo;
02952 
02953         turn_info turn_data(gamestate_, status_, *gui_, const_cast<gamemap&>(map_), teams_, team_num, units_, dummy_sender, dummy_undo);
02954         ai_interface::info info(*gui_, map_, units_, teams_, team_num, status_, turn_data, gamestate_);
02955         formula_ai eval(info);
02956         try {
02957             add_chat_message(time(NULL), _("ai"), 0, eval.evaluate(str));
02958         } catch(...) {
02959             add_chat_message(time(NULL), _("ai"), 0, "ERROR IN FORMULA");
02960         }
02961     }
02962 
02963     void menu_handler::user_command()
02964     {
02965         textbox_info_.show(gui::TEXTBOX_COMMAND,sgettext("prompt^Command:"), "", false, *gui_);
02966     }
02967 
02968     void menu_handler::custom_command(mouse_handler& mousehandler, const unsigned int team_num)
02969     {
02970         std::vector<std::string> commands = utils::split(preferences::custom_command(), ';');
02971         std::vector<std::string>::iterator c = commands.begin();
02972         for (; c != commands.end() ; ++c) {
02973             do_command(*c, team_num, mousehandler);
02974         }
02975     }
02976 
02977     void menu_handler::ai_formula()
02978     {
02979         std::cerr << "showing ai formula...\n";
02980         textbox_info_.show(gui::TEXTBOX_AI,sgettext("prompt^Command:"), "", false, *gui_);
02981     }
02982 
02983     void menu_handler::clear_messages()
02984     {
02985         gui_->clear_chat_messages();    // also clear debug-messages and WML-error-messages
02986     }
02987 
02988 #ifdef USRCMD2
02989     // not used yet - for future hotkey-commands:
02990     void menu_handler::user_command_2()
02991     {
02992         gui::message_dialog(*gui_, "Test", "User-Command#2").show();
02993         //sound::play_bell(game_config::sounds::turn_bell);
02994         sound::play_bell("bell.wav");
02995     }
02996 
02997     void menu_handler::user_command_3()
02998     {
02999         gui::message_dialog(*gui_, "Info", _("User-Command#3")).show();
03000         //gui::show_error_message(disp(), "User-Command#3");
03001         //sound::play_sound("heal.wav");
03002         sound::play_sound("select.wav");
03003     }
03004 #endif
03005 
03006     void menu_handler::change_side_controller(const std::string& side, const std::string& player, bool own_side)
03007     {
03008         config cfg;
03009         config& change = cfg.add_child("change_controller");
03010         change["side"] = side;
03011         change["player"] = player;
03012 
03013         if(own_side) {
03014             change["own_side"] = "yes";
03015         }
03016 
03017         network::send_data(cfg, 0, true);
03018     }
03019 } // end namespace events
03020 

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