menu_events.cpp

Go to the documentation of this file.
00001 /* $Id: menu_events.cpp 54273 2012-05-20 21:56:37Z jamit $ */
00002 /*
00003    Copyright (C) 2006 - 2012 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 as published by
00009    the Free Software Foundation; either version 2 of the License, or
00010    (at your option) any later version.
00011    This program is distributed in the hope that it will be useful,
00012    but WITHOUT ANY WARRANTY.
00013 
00014    See the COPYING file for more details.
00015 */
00016 
00017 /**
00018  * @file
00019  * Operations activated from menus/hotkeys while playing a game.
00020  * E.g. Unitlist, status_table, save_game, save_map, chat, show_help, etc.
00021  */
00022 
00023 #include "global.hpp"
00024 
00025 #include "builder.hpp"
00026 #include "ai/manager.hpp"
00027 #include "dialogs.hpp"
00028 #include "formatter.hpp"
00029 #include "filechooser.hpp"
00030 #include "foreach.hpp"
00031 #include "game_end_exceptions.hpp"
00032 #include "game_events.hpp"
00033 #include "game_preferences.hpp"
00034 #include "gettext.hpp"
00035 #include "gui/dialogs/chat_log.hpp"
00036 #include "gui/dialogs/edit_label.hpp"
00037 #include "gui/dialogs/message.hpp"
00038 #include "gui/dialogs/transient_message.hpp"
00039 #include "gui/dialogs/wml_message.hpp"
00040 #include "gui/dialogs/gamestate_inspector.hpp"
00041 #include "gui/dialogs/mp_change_control.hpp"
00042 #include "gui/dialogs/data_manage.hpp"
00043 #include "gui/dialogs/simple_item_selector.hpp"
00044 #include "gui/dialogs/unit_create.hpp"
00045 #include "gui/widgets/settings.hpp"
00046 #include "gui/widgets/window.hpp"
00047 #include "help.hpp"
00048 #include "log.hpp"
00049 #include "map.hpp"
00050 #include "map_label.hpp"
00051 #include "marked-up_text.hpp"
00052 #include "menu_events.hpp"
00053 #include "mouse_events.hpp"
00054 #include "play_controller.hpp"
00055 #include "preferences_display.hpp"
00056 #include "replay.hpp"
00057 #include "resources.hpp"
00058 #include "savegame.hpp"
00059 #include "sound.hpp"
00060 #include "statistics_dialog.hpp"
00061 #include "unit_display.hpp"
00062 #include "wml_separators.hpp"
00063 #include "formula_string_utils.hpp"
00064 #include "scripting/lua.hpp"
00065 #include "whiteboard/manager.hpp"
00066 #include "widgets/combo.hpp"
00067 
00068 static lg::log_domain log_engine("engine");
00069 #define ERR_NG LOG_STREAM(err, log_engine)
00070 #define LOG_NG LOG_STREAM(info, log_engine)
00071 
00072 namespace events{
00073 
00074 class delete_recall_unit : public gui::dialog_button_action
00075 {
00076 public:
00077     delete_recall_unit(game_display& disp, gui::filter_textbox& filter, const std::vector<const unit*>& units) : disp_(disp), filter_(filter), units_(units) {}
00078 private:
00079     gui::dialog_button_action::RESULT button_pressed(int menu_selection);
00080 
00081     game_display& disp_;
00082     gui::filter_textbox& filter_;
00083     const std::vector<const unit*>& units_;
00084 };
00085 
00086 gui::dialog_button_action::RESULT delete_recall_unit::button_pressed(int menu_selection)
00087 {
00088     const std::vector<std::pair<int, int> >& param = std::vector<std::pair<int, int> >();
00089     param.back();
00090     const size_t index = size_t(filter_.get_index(menu_selection));
00091     if(index < units_.size()) {
00092         const unit &u = *(units_[index]);
00093 
00094         //If the unit is of level > 1, or is close to advancing,
00095         //we warn the player about it
00096         std::stringstream message;
00097         if (u.loyal()) {
00098             message << _("This unit is loyal and requires no upkeep.") << ' ' << (u.gender() == unit_race::MALE ? _("Do you really want to dismiss him?")
00099                     : _("Do you really want to dismiss her?"));
00100         } else if(u.level() > 1) {
00101             message << _("This unit is an experienced one, having advanced levels.") << ' ' << (u.gender() == unit_race::MALE ? _("Do you really want to dismiss him?")
00102                     : _("Do you really want to dismiss her?"));
00103 
00104         } else if(u.experience() > u.max_experience()/2) {
00105             message << _("This unit is close to advancing a level.") << ' ' << (u.gender() == unit_race::MALE ? _("Do you really want to dismiss him?")
00106                     : _("Do you really want to dismiss her?"));
00107         }
00108 
00109         if(!message.str().empty()) {
00110             const int res = gui2::show_message(disp_.video(), _("Dismiss Unit"), message.str(), gui2::tmessage::yes_no_buttons);
00111             if(res == gui2::twindow::CANCEL) {
00112                 return gui::CONTINUE_DIALOG;
00113             }
00114         }
00115         // Remove the item from filter_textbox memory
00116         filter_.delete_item(index);
00117         //add dismissal to the undo stack
00118         resources::undo_stack->push_back(undo_action(u, map_location(), map_location(), undo_action::DISMISS));
00119 
00120         //remove the unit from the recall list
00121         std::vector<unit>& recall_list = (*resources::teams)[u.side() -1].recall_list();
00122         assert(!recall_list.empty());
00123         std::vector<unit>::iterator dismissed_unit =
00124                 find_if_matches_id(recall_list, u.id());
00125         assert(dismissed_unit != recall_list.end());
00126         recall_list.erase(dismissed_unit);
00127         recorder.add_disband(dismissed_unit->id());
00128 
00129         //clear the redo stack to avoid duplication of dismissals
00130         resources::redo_stack->clear();
00131         return gui::DELETE_ITEM;
00132     } else {
00133         return gui::CONTINUE_DIALOG;
00134     }
00135 }
00136 
00137 menu_handler::menu_handler(game_display* gui, unit_map& units, std::vector<team>& teams,
00138         const config& level, const gamemap& map,
00139         const config& game_config, const tod_manager& tod_mng, game_state& gamestate) :
00140     gui_(gui),
00141     units_(units),
00142     teams_(teams),
00143     level_(level),
00144     map_(map),
00145     game_config_(game_config),
00146     tod_manager_(tod_mng),
00147     gamestate_(gamestate),
00148     textbox_info_(),
00149     last_search_(),
00150     last_search_hit_(),
00151     last_recruit_()
00152 {
00153 }
00154 
00155 menu_handler::~menu_handler()
00156 {
00157 }
00158 
00159 gui::floating_textbox& menu_handler::get_textbox(){
00160     return textbox_info_;
00161 }
00162 
00163 std::string menu_handler::get_title_suffix(int side_num)
00164 {
00165     int controlled_recruiters = 0;
00166     for(size_t i = 0; i < teams_.size(); ++i) {
00167         if(teams_[i].is_human() && !teams_[i].recruits().empty()
00168         && units_.find_leader(i + 1) != units_.end()) {
00169         ++controlled_recruiters;
00170         }
00171     }
00172     std::stringstream msg;
00173     if(controlled_recruiters >= 2) {
00174         unit_map::const_iterator leader = units_.find_leader(side_num);
00175         if (leader != units_.end() && !leader->name().empty()) {
00176             msg << " (" << leader->name(); msg << ")";
00177         }
00178     }
00179     return msg.str();
00180 }
00181 
00182 void menu_handler::objectives(int side_num)
00183 {
00184     config cfg;
00185     cfg["side"] = str_cast(side_num);
00186     game_events::handle_event_command("show_objectives",
00187         game_events::queued_event("_from_interface", map_location(),
00188             map_location(), config()), vconfig(cfg));
00189     team &current_team = teams_[side_num - 1];
00190     dialogs::show_objectives(level_, current_team.objectives());
00191     current_team.reset_objectives_changed();
00192 }
00193 
00194 void menu_handler::show_statistics(int side_num)
00195 {
00196     team &current_team = teams_[side_num - 1];
00197     // Current Player name
00198     const std::string &player = current_team.current_player();
00199     //add player's name to title of dialog
00200     std::stringstream title_str;
00201     title_str <<  _("Statistics") << " (" << player << ")";
00202     statistics_dialog stats_dialog(*gui_, title_str.str(),
00203         side_num, current_team.save_id(), player);
00204     stats_dialog.show();
00205 }
00206 
00207 void menu_handler::unit_list()
00208 {
00209     const std::string heading = std::string(1,HEADING_PREFIX) +
00210                                 _("Type")          + COLUMN_SEPARATOR + // 0
00211                                 _("Name")          + COLUMN_SEPARATOR + // 1
00212                                 _("Moves")         + COLUMN_SEPARATOR + // 2
00213                                 _("Status")        + COLUMN_SEPARATOR + // 3
00214                                 _("HP")            + COLUMN_SEPARATOR + // 4
00215                                 _("Level^Lvl.")    + COLUMN_SEPARATOR + // 5
00216                                 _("XP")            + COLUMN_SEPARATOR + // 6
00217                                 _("unit list^Traits");                  // 7
00218 
00219     gui::menu::basic_sorter sorter;
00220     sorter.set_alpha_sort(0).set_alpha_sort(1).set_numeric_sort(2);
00221     sorter.set_alpha_sort(3).set_numeric_sort(4).set_level_sort(5, 6);
00222     sorter.set_xp_sort(6).set_alpha_sort(7);
00223 
00224     std::vector<std::string> items;
00225     items.push_back(heading);
00226 
00227     std::vector<map_location> locations_list;
00228     std::vector<unit> units_list;
00229 
00230     int selected = 0;
00231 
00232     for(unit_map::const_iterator i = units_.begin(); i != units_.end(); ++i) {
00233         if (i->side() != gui_->viewing_side())
00234             continue;
00235 
00236         std::stringstream row;
00237         // If a unit is already selected on the map, we do the same in the unit list dialog
00238         if (gui_->selected_hex() == i->get_location()) {
00239              row << DEFAULT_ITEM;
00240              selected = units_list.size();
00241         }
00242         // If unit is leader, show name in special color, e.g. gold/silver
00243         /** @todo TODO: hero just has overlay "misc/hero-icon.png" - needs an ability to query */
00244 
00245         if (i->can_recruit() ) {
00246             row << "<205,173,0>";   // gold3
00247         }
00248         row << i->type_name() << COLUMN_SEPARATOR;
00249         if (i->can_recruit() ) {
00250             row << "<205,173,0>";   // gold3
00251         }
00252         row << i->name()   << COLUMN_SEPARATOR;
00253 
00254         // display move left (0=red, moved=yellow, not moved=green)
00255         if (i->movement_left() == 0) {
00256             row << font::RED_TEXT;
00257         } else if (i->movement_left() < i->total_movement() ) {
00258             row << "<255,255,0>";
00259         } else {
00260             row << font::GREEN_TEXT;
00261         }
00262         row << i->movement_left() << '/' << i->total_movement() << COLUMN_SEPARATOR;
00263 
00264         // show icons if unit is slowed, poisoned, petrified, invisible:
00265         if(i->get_state(unit::STATE_PETRIFIED))
00266             row << IMAGE_PREFIX << "misc/petrified.png"    << IMG_TEXT_SEPARATOR;
00267         if(i->get_state(unit::STATE_POISONED))
00268             row << IMAGE_PREFIX << "misc/poisoned.png" << IMG_TEXT_SEPARATOR;
00269         if(i->get_state(unit::STATE_SLOWED))
00270             row << IMAGE_PREFIX << "misc/slowed.png"   << IMG_TEXT_SEPARATOR;
00271         if(i->invisible(i->get_location(),false))
00272             row << IMAGE_PREFIX << "misc/invisible.png";
00273         row << COLUMN_SEPARATOR;
00274 
00275         // Display HP
00276         // see also unit_preview_pane in dialogs.cpp
00277         row << font::color2markup(i->hp_color());
00278         row << i->hitpoints()  << '/' << i->max_hitpoints() << COLUMN_SEPARATOR;
00279 
00280         // Show units of level (0=gray, 1 normal, 2 bold, 2+ bold&wbright)
00281         int level = i->level();
00282         if(level < 1) {
00283             row << "<150,150,150>";
00284         } else if(level == 1) {
00285             row << font::NORMAL_TEXT;
00286         } else if(level == 2) {
00287             row << font::BOLD_TEXT;
00288         } else if(level > 2 ) {
00289             row << font::BOLD_TEXT << "<255,255,255>";
00290         }
00291         row << level << COLUMN_SEPARATOR;
00292 
00293         // Display XP
00294         row << font::color2markup(i->xp_color());
00295         row << i->experience() << "/";
00296         if (i->can_advance()) {
00297             row << i->max_experience();
00298         } else {
00299             row << "-";
00300         }
00301         row << COLUMN_SEPARATOR;
00302 
00303         // TODO: show 'loyal' in green / xxx in red  //  how to handle translations ??
00304         row << utils::join(i->trait_names(), ", ");
00305         items.push_back(row.str());
00306 
00307         locations_list.push_back(i->get_location());
00308         units_list.push_back(*i);
00309     }
00310 
00311     {
00312         dialogs::units_list_preview_pane unit_preview(units_list);
00313         unit_preview.set_selection(selected);
00314 
00315         gui::dialog umenu(*gui_, _("Unit List"), "", gui::NULL_DIALOG);
00316         umenu.set_menu(items, &sorter);
00317         umenu.add_pane(&unit_preview);
00318         //sort by type name
00319         umenu.get_menu().sort_by(0);
00320 
00321         umenu.add_button(new gui::standard_dialog_button(gui_->video(), _("Scroll To"), 0, false),
00322                          gui::dialog::BUTTON_STANDARD);
00323         umenu.add_button(new gui::standard_dialog_button(gui_->video(), _("Close"), 1, true),
00324                          gui::dialog::BUTTON_STANDARD);
00325         umenu.set_basic_behavior(gui::OK_CANCEL);
00326         selected = umenu.show();
00327     } // this will kill the dialog before scrolling
00328 
00329     if(selected >= 0 && selected < int(locations_list.size())) {
00330         const map_location& loc = locations_list[selected];
00331         gui_->scroll_to_tile(loc,game_display::WARP);
00332         gui_->select_hex(loc);
00333     }
00334 }
00335 
00336 namespace {
00337 class leader_scroll_dialog : public gui::dialog {
00338 public:
00339     leader_scroll_dialog(display &disp, const std::string &title,
00340             std::vector<bool> &leader_bools, int selected,
00341             gui::DIALOG_RESULT extra_result) :
00342         dialog(disp, title, "", gui::NULL_DIALOG),
00343         scroll_btn_(new gui::standard_dialog_button(disp.video(), _("Scroll To"), 0, false)),
00344         leader_bools_(leader_bools),
00345         extra_result_(extra_result)
00346     {
00347         scroll_btn_->enable(leader_bools[selected]);
00348         add_button(scroll_btn_, gui::dialog::BUTTON_STANDARD);
00349         add_button(new gui::standard_dialog_button(disp.video(),
00350             _("Close"), 1, true), gui::dialog::BUTTON_STANDARD);
00351     }
00352     void action(gui::dialog_process_info &info) {
00353         const bool leader_bool = leader_bools_[get_menu().selection()];
00354         scroll_btn_->enable(leader_bool);
00355         if(leader_bool && (info.double_clicked || (!info.key_down
00356         && (info.key[SDLK_RETURN] || info.key[SDLK_KP_ENTER])))) {
00357             set_result(get_menu().selection());
00358         } else if(!info.key_down && info.key[SDLK_ESCAPE]) {
00359             set_result(gui::CLOSE_DIALOG);
00360         } else if(!info.key_down && info.key[SDLK_SPACE]) {
00361             set_result(extra_result_);
00362         } else if(result() == gui::CONTINUE_DIALOG) {
00363             dialog::action(info);
00364         }
00365     }
00366 private:
00367     gui::standard_dialog_button *scroll_btn_;
00368     std::vector<bool> &leader_bools_;
00369     gui::DIALOG_RESULT extra_result_;
00370 };
00371 } //end anonymous namespace
00372 void menu_handler::status_table(int selected)
00373 {
00374     std::stringstream heading;
00375     heading << HEADING_PREFIX << _("Leader") << COLUMN_SEPARATOR << ' ' << COLUMN_SEPARATOR
00376             << _("Team")         << COLUMN_SEPARATOR
00377             << _("Gold")         << COLUMN_SEPARATOR
00378             << _("Villages")     << COLUMN_SEPARATOR
00379             << _("status^Units") << COLUMN_SEPARATOR
00380             << _("Upkeep")       << COLUMN_SEPARATOR
00381             << _("Income");
00382 
00383     gui::menu::basic_sorter sorter;
00384     sorter.set_redirect_sort(0,1).set_alpha_sort(1).set_alpha_sort(2).set_numeric_sort(3)
00385           .set_numeric_sort(4).set_numeric_sort(5).set_numeric_sort(6).set_numeric_sort(7);
00386 
00387     std::vector<std::string> items;
00388     std::vector<bool> leader_bools;
00389     items.push_back(heading.str());
00390 
00391     const team& viewing_team = teams_[gui_->viewing_team()];
00392 
00393     unsigned total_villages = 0;
00394     // a variable to check if there are any teams to show in the table
00395     bool status_table_empty = true;
00396 
00397     //if the player is under shroud or fog, they don't get
00398     //to see details about the other sides, only their own
00399     //side, allied sides and a ??? is shown to demonstrate
00400     //lack of information about the other sides But he see
00401     //all names with in colors
00402     for(size_t n = 0; n != teams_.size(); ++n) {
00403         if(teams_[n].hidden()) {
00404             continue;
00405         }
00406         status_table_empty=false;
00407 
00408         const bool known = viewing_team.knows_about_team(n, network::nconnections() > 0);
00409         const bool enemy = viewing_team.is_enemy(n+1);
00410 
00411         std::stringstream str;
00412 
00413         const team_data data = calculate_team_data(teams_[n],n+1);
00414 
00415         unit_map::const_iterator leader = units_.find_leader(n + 1);
00416         std::string leader_name;
00417         //output the number of the side first, and this will
00418         //cause it to be displayed in the correct color
00419         if(leader != units_.end()) {
00420             const bool fogged = viewing_team.fogged(leader->get_location());
00421             // Add leader image. If it's fogged
00422             // show only a random leader image.
00423             if (!fogged || known || game_config::debug) {
00424                 str << IMAGE_PREFIX << leader->absolute_image();
00425                 leader_bools.push_back(true);
00426                 leader_name = leader->name();
00427             } else {
00428                 str << IMAGE_PREFIX << std::string("units/unknown-unit.png");
00429                 leader_bools.push_back(false);
00430                 leader_name = "Unknown";
00431             }
00432         if (gamestate_.classification().campaign_type == "multiplayer")
00433                 leader_name = teams_[n].current_player();
00434 
00435 #ifndef LOW_MEM
00436             str << leader->image_mods();
00437 #endif
00438         } else {
00439             leader_bools.push_back(false);
00440         }
00441         str << COLUMN_SEPARATOR << team::get_side_highlight(n)
00442             << leader_name << COLUMN_SEPARATOR
00443             << (data.teamname.empty() ? teams_[n].team_name() : data.teamname)
00444             << COLUMN_SEPARATOR;
00445 
00446         if(!known && !game_config::debug) {
00447             // We don't spare more info (only name)
00448             // so let's go on next side ...
00449             items.push_back(str.str());
00450             continue;
00451         }
00452 
00453         if(game_config::debug) {
00454             str << utils::half_signed_value(data.gold) << COLUMN_SEPARATOR;
00455         } else if(enemy && viewing_team.uses_fog()) {
00456             str << ' ' << COLUMN_SEPARATOR;
00457         } else {
00458             str << utils::half_signed_value(data.gold) << COLUMN_SEPARATOR;
00459         }
00460         str << data.villages;
00461                 if(!(viewing_team.uses_fog() || viewing_team.uses_shroud())) {
00462                         str << "/" << map_.villages().size();
00463                 }
00464         str << COLUMN_SEPARATOR
00465             << data.units << COLUMN_SEPARATOR << data.upkeep << COLUMN_SEPARATOR
00466             << (data.net_income < 0 ? font::BAD_TEXT : font::NULL_MARKUP) << utils::signed_value(data.net_income);
00467         total_villages += data.villages;
00468         items.push_back(str.str());
00469     }
00470     if (total_villages > map_.villages().size()) {
00471         ERR_NG << "Logic error: map has " << map_.villages().size() << " villages but status table shows " << total_villages << " owned in total\n";
00472     }
00473 
00474     if (status_table_empty)
00475     {
00476         // no sides to show - display empty table
00477         std::stringstream str;
00478         str << " ";
00479         for (int i=0;i<7;++i)
00480             str << COLUMN_SEPARATOR << " ";
00481         leader_bools.push_back(false);
00482         items.push_back(str.str());
00483     }
00484     int result = 0;
00485     {
00486         leader_scroll_dialog slist(*gui_, _("Current Status"), leader_bools, selected, gui::DIALOG_FORWARD);
00487         slist.add_button(new gui::dialog_button(gui_->video(), _("More >"),
00488                                                  gui::button::TYPE_PRESS, gui::DIALOG_FORWARD),
00489                                                  gui::dialog::BUTTON_EXTRA_LEFT);
00490         slist.set_menu(items, &sorter);
00491         slist.get_menu().move_selection(selected);
00492         result = slist.show();
00493         selected = slist.get_menu().selection();
00494     } // this will kill the dialog before scrolling
00495 
00496     if (result >= 0)
00497         gui_->scroll_to_leader(units_, selected+1);
00498     else if (result == gui::DIALOG_FORWARD)
00499         scenario_settings_table(selected);
00500 }
00501 
00502 void menu_handler::scenario_settings_table(int selected)
00503 {
00504     std::stringstream heading;
00505     heading << HEADING_PREFIX << _("scenario settings^Leader") << COLUMN_SEPARATOR
00506             << COLUMN_SEPARATOR
00507             << _("scenario settings^Side")              << COLUMN_SEPARATOR
00508             << _("scenario settings^Start\nGold")       << COLUMN_SEPARATOR
00509             << _("scenario settings^Base\nIncome")      << COLUMN_SEPARATOR
00510             << _("scenario settings^Gold Per\nVillage") << COLUMN_SEPARATOR
00511             << _("scenario settings^Support Per\nVillage") << COLUMN_SEPARATOR
00512             << _("scenario settings^Fog")               << COLUMN_SEPARATOR
00513             << _("scenario settings^Shroud");
00514 
00515     gui::menu::basic_sorter sorter;
00516     sorter.set_redirect_sort(0,1).set_alpha_sort(1).set_numeric_sort(2)
00517           .set_numeric_sort(3).set_numeric_sort(4).set_numeric_sort(5)
00518           .set_numeric_sort(6).set_alpha_sort(7).set_alpha_sort(8);
00519 
00520     std::vector<std::string> items;
00521     std::vector<bool> leader_bools;
00522     items.push_back(heading.str());
00523 
00524     const team& viewing_team = teams_[gui_->viewing_team()];
00525     bool settings_table_empty = true;
00526     bool fogged;
00527 
00528     for(size_t n = 0; n != teams_.size(); ++n) {
00529         if(teams_[n].hidden()) {
00530             continue;
00531         }
00532         settings_table_empty = false;
00533 
00534         std::stringstream str;
00535         unit_map::const_iterator leader = units_.find_leader(n + 1);
00536 
00537         if(leader != units_.end()) {
00538             // Add leader image. If it's fogged
00539             // show only a random leader image.
00540             fogged=viewing_team.fogged(leader->get_location());
00541             if (!fogged || viewing_team.knows_about_team(n, network::nconnections() > 0) || game_config::debug) {
00542                 str << IMAGE_PREFIX << leader->absolute_image();
00543                 leader_bools.push_back(true);
00544             } else {
00545                 str << IMAGE_PREFIX << std::string("units/unknown-unit.png");
00546                 leader_bools.push_back(false);
00547             }
00548 #ifndef LOW_MEM
00549             str << "~RC(" << leader->team_color() << '>'
00550                 << team::get_side_color_index(n+1) << ")";
00551 #endif
00552         } else {
00553             leader_bools.push_back(false);
00554         }
00555 
00556         str << COLUMN_SEPARATOR << team::get_side_highlight(n)
00557             << teams_[n].current_player() << COLUMN_SEPARATOR
00558             << n + 1 << COLUMN_SEPARATOR
00559             << teams_[n].start_gold() << COLUMN_SEPARATOR
00560             << teams_[n].base_income() << COLUMN_SEPARATOR
00561             << teams_[n].village_gold() << COLUMN_SEPARATOR
00562             << teams_[n].village_support() << COLUMN_SEPARATOR
00563             << (teams_[n].uses_fog()    ? _("yes") : _("no")) << COLUMN_SEPARATOR
00564             << (teams_[n].uses_shroud() ? _("yes") : _("no")) << COLUMN_SEPARATOR;
00565 
00566         items.push_back(str.str());
00567     }
00568 
00569     if (settings_table_empty)
00570     {
00571         // no sides to show - display empty table
00572         std::stringstream str;
00573         for (int i=0;i<8;++i)
00574             str << " " << COLUMN_SEPARATOR;
00575         leader_bools.push_back(false);
00576         items.push_back(str.str());
00577     }
00578         int result = 0;
00579         {
00580             leader_scroll_dialog slist(*gui_, _("Scenario Settings"), leader_bools, selected, gui::DIALOG_BACK);
00581             slist.set_menu(items, &sorter);
00582             slist.get_menu().move_selection(selected);
00583             slist.add_button(new gui::dialog_button(gui_->video(), _(" < Back"),
00584                                                      gui::button::TYPE_PRESS, gui::DIALOG_BACK),
00585                                                      gui::dialog::BUTTON_EXTRA_LEFT);
00586             result = slist.show();
00587             selected = slist.get_menu().selection();
00588         } // this will kill the dialog before scrolling
00589 
00590         if (result >= 0)
00591             gui_->scroll_to_leader(units_, selected+1);
00592         else if (result == gui::DIALOG_BACK)
00593             status_table(selected);
00594 }
00595 
00596 void menu_handler::save_map()
00597 {
00598     std::string input_name = get_dir(get_dir(get_user_data_dir() + "/editor") + "/maps/");
00599     int res = 0;
00600     int overwrite = 1;
00601     do {
00602         res = dialogs::show_file_chooser_dialog_save(*gui_, input_name, _("Save the Map As"));
00603         if (res == 0) {
00604 
00605             if (file_exists(input_name)) {
00606                 const int res = gui2::show_message((*gui_).video(), "", _("The map already exists. Do you want to overwrite it?"), gui2::tmessage::yes_no_buttons);
00607                 overwrite = res == gui2::twindow::CANCEL ? 1 : 0;
00608             }
00609             else
00610                 overwrite = 0;
00611         }
00612     } while (res == 0 && overwrite != 0);
00613 
00614     // Try to save the map, if it fails we reset the filename.
00615     if (res == 0) {
00616         try {
00617             config file;
00618             config& map = file.add_child("map");
00619             map_.write(map);
00620             std::stringstream str;
00621             str << file;
00622             write_file(input_name, str.str());
00623             gui2::show_transient_message(gui_->video(), "", _("Map saved."));
00624         } catch (io_exception& e) {
00625             utils::string_map symbols;
00626             symbols["msg"] = e.what();
00627             const std::string msg = vgettext("Could not save the map: $msg",symbols);
00628             gui2::show_transient_message(gui_->video(), "", msg);
00629         }
00630     }
00631 }
00632 
00633 void menu_handler::preferences()
00634 {
00635     preferences::show_preferences_dialog(*gui_, game_config_);
00636     // Needed after changing fullscreen/windowed mode or display resolution
00637     gui_->redraw_everything();
00638 }
00639 
00640 void menu_handler::show_chat_log()
00641 {
00642         config c;
00643         c["name"] = "prototype of chat log";
00644         gui2::tchat_log chat_log_dialog(vconfig(c),&recorder);
00645         chat_log_dialog.show(resources::screen->video());
00646         //std::string text = recorder.build_chat_log();
00647         //gui::show_dialog(*gui_,NULL,_("Chat Log"),"",gui::CLOSE_ONLY,NULL,NULL,"",&text);
00648 
00649 }
00650 
00651 void menu_handler::show_help()
00652 {
00653     help::show_help(*gui_);
00654 }
00655 
00656 void menu_handler::speak()
00657 {
00658     textbox_info_.show(gui::TEXTBOX_MESSAGE,_("Message:"),
00659         has_friends() ? is_observer() ? _("Send to observers only") : _("Send to allies only")
00660                       : "", preferences::message_private(), *gui_);
00661 }
00662 
00663 void menu_handler::whisper()
00664 {
00665     preferences::set_message_private(true);
00666     speak();
00667 }
00668 
00669 void menu_handler::shout()
00670 {
00671     preferences::set_message_private(false);
00672     speak();
00673 }
00674 
00675 bool menu_handler::has_friends() const
00676 {
00677     if(is_observer()) {
00678         return !gui_->observers().empty();
00679     }
00680 
00681     for(size_t n = 0; n != teams_.size(); ++n) {
00682         if(n != gui_->viewing_team() && teams_[gui_->viewing_team()].team_name() == teams_[n].team_name() && teams_[n].is_network()) {
00683             return true;
00684         }
00685     }
00686 
00687     return false;
00688 }
00689 
00690 void menu_handler::recruit(int side_num, const map_location &last_hex)
00691 {
00692     team &current_team = teams_[side_num - 1];
00693 
00694     std::vector<const unit_type*> sample_units;
00695 
00696     gui_->draw(); //clear the old menu
00697     std::vector<std::string> item_keys;
00698     std::vector<std::string> items;
00699 
00700     std::set<std::string> recruits = get_recruits_for_location(side_num, last_hex);
00701 
00702     for(std::set<std::string>::const_iterator it = recruits.begin(); it != recruits.end(); ++it) {
00703         const unit_type *type = unit_types.find(*it);
00704         if (!type) {
00705             ERR_NG << "could not find unit '" << *it << "'\n";
00706             return;
00707         }
00708 
00709         item_keys.push_back(*it);
00710 
00711         char prefix;
00712         { wb::future_map future; // so gold takes into account planned spending
00713             int wb_gold = resources::whiteboard->get_spent_gold_for(side_num);
00714             //display units that we can't afford to recruit in red
00715             prefix = (type->cost() > current_team.gold() - wb_gold
00716                     ? font::BAD_TEXT : font::NULL_MARKUP);
00717         } // end planned unit map scope
00718 
00719         std::stringstream description;
00720         description << font::IMAGE << type->image();
00721 #ifndef LOW_MEM
00722         description << "~RC(" << type->flag_rgb() << '>'
00723             << team::get_side_color_index(side_num) << ')';
00724 #endif
00725         description << COLUMN_SEPARATOR << font::LARGE_TEXT << prefix << type->type_name() << "\n"
00726                 << prefix << type->cost() << " " << sngettext("unit^Gold", "Gold", type->cost());
00727 
00728         items.push_back(description.str());
00729         sample_units.push_back(type);
00730     }
00731 
00732     if(sample_units.empty()) {
00733         gui2::show_transient_message(gui_->video(),"",_("You have no units available to recruit."));
00734         return;
00735     }
00736 
00737     int recruit_res = 0;
00738 
00739     {
00740         dialogs::unit_types_preview_pane unit_preview(
00741             sample_units, NULL, side_num);
00742         std::vector<gui::preview_pane*> preview_panes;
00743         preview_panes.push_back(&unit_preview);
00744 
00745         gui::menu::basic_sorter sorter;
00746         sorter.set_alpha_sort(1);
00747 
00748         gui::dialog rmenu(*gui_, _("Recruit") + get_title_suffix(side_num),
00749                   _("Select unit:") + std::string("\n"),
00750                   gui::OK_CANCEL,
00751                   gui::dialog::default_style);
00752         rmenu.add_button(new help::help_button(*gui_,"recruit_and_recall"),
00753             gui::dialog::BUTTON_HELP);
00754         rmenu.set_menu(items, &sorter);
00755         rmenu.get_menu().sort_by(1); // otherwise it's unsorted by default
00756         rmenu.set_panes(preview_panes);
00757         recruit_res = rmenu.show();
00758     }
00759 
00760     if(recruit_res != -1) {
00761         do_recruit(item_keys[recruit_res], side_num, last_hex);
00762     }
00763 }
00764 
00765 
00766 void menu_handler::repeat_recruit(int side_num, const map_location &last_hex)
00767 {
00768     if(last_recruit_.empty() == false)
00769         do_recruit(last_recruit_, side_num, last_hex);
00770 }
00771 
00772 bool menu_handler::do_recruit(const std::string &name, int side_num,
00773     const map_location &last_hex)
00774 {
00775     team &current_team = teams_[side_num - 1];
00776 
00777     //search for the unit to be recruited in recruits
00778     int recruit_num = 0;
00779     std::set<std::string> recruits = get_recruits_for_location(side_num, last_hex);
00780 
00781     for(std::set<std::string>::const_iterator r = recruits.begin(); ; ++r) {
00782         if (r == recruits.end()) {
00783             return false;
00784         }
00785 
00786         if (name == *r) {
00787             break;
00788         }
00789         ++recruit_num;
00790     }
00791 
00792     const unit_type *u_type = unit_types.find(name);
00793     assert(u_type);
00794 
00795     if (u_type->cost() > current_team.gold() - resources::whiteboard->get_spent_gold_for(side_num)) {
00796         gui2::show_transient_message(gui_->video(), "",
00797             _("You don’t have enough gold to recruit that unit"));
00798         return false;
00799     }
00800 
00801     last_recruit_ = name;
00802     const events::command_disabler disable_commands;
00803 
00804     map_location loc = last_hex;
00805     map_location recruited_from = map_location::null_location;
00806     std::string msg;
00807     { wb::future_map_if_active future; //< start planned unit map scope if in planning mode
00808         msg = find_recruit_location(side_num, loc, recruited_from, u_type->id());
00809     } // end planned unit map scope
00810     if (!msg.empty()) {
00811         gui2::show_transient_message(gui_->video(), "", msg);
00812         return false;
00813     }
00814 
00815     if (!resources::whiteboard->save_recruit(name, side_num, loc)) {
00816         //create a unit with traits
00817         recorder.add_recruit(recruit_num, loc, recruited_from);
00818         const unit new_unit(u_type, side_num, true);
00819         place_recruit(new_unit, loc, recruited_from, false, true);
00820         current_team.spend_gold(u_type->cost());
00821         statistics::recruit_unit(new_unit);
00822 
00823         //MP_COUNTDOWN grant time bonus for recruiting
00824         current_team.set_action_bonus_count(1 + current_team.action_bonus_count());
00825 
00826         resources::redo_stack->clear();
00827         assert(new_unit.type());
00828 
00829         // Dissallow undoing of recruits. Can be enabled again once the unit's
00830         // description= key doesn't use random anymore.
00831         const bool shroud_cleared = clear_shroud(side_num);
00832         if (shroud_cleared || new_unit.type()->genders().size() > 1
00833             || new_unit.type()->has_random_traits()) {
00834             clear_undo_stack(side_num);
00835         } else {
00836             resources::undo_stack->push_back(undo_action(new_unit, loc, recruited_from, undo_action::RECRUIT));
00837         }
00838 
00839         gui_->redraw_minimap();
00840         gui_->invalidate_game_status();
00841         gui_->invalidate(loc);
00842         recorder.add_checksum_check(loc);
00843         return true;
00844     }
00845     return false;
00846 }
00847 
00848 void menu_handler::recall(int side_num, const map_location &last_hex)
00849 {
00850     if (level_["disallow_recall"].to_bool()) {
00851         gui2::show_transient_message(gui_->video(),"",_("You are separated from your soldiers and may not recall them"));
00852         return;
00853     }
00854 
00855     team &current_team = teams_[side_num - 1];
00856     if(!current_team.persistent()) {
00857         ERR_NG << "cannot recall a unit for side " << side_num
00858             << ", which has no recall list!\n";
00859         return;
00860     }
00861 
00862     std::vector<const unit*> recall_list_team;
00863     { wb::future_map future; // ensures recall list has planned recalls removed
00864         recall_list_team = get_recalls_for_location(side_num, last_hex);
00865     }
00866 
00867     gui_->draw(); //clear the old menu
00868 
00869 
00870     DBG_WB <<"menu_handler::recall: Contents of wb-modified recall list:\n";
00871     foreach(const unit* unit, recall_list_team)
00872     {
00873         DBG_WB << unit->name() << " [" << unit->id() <<"]\n";
00874     }
00875 
00876     if(current_team.recall_list().empty()) {
00877         gui2::show_transient_message(gui_->video(), "",
00878             _("There are no troops available to recall\n(You must have"
00879             " veteran survivors from a previous scenario)"));
00880         return;
00881     }
00882     if(recall_list_team.empty()) {
00883         gui2::show_transient_message(gui_->video(), "",
00884             _("You currently can't recall at the highlighted location"));
00885         return;
00886     }
00887 
00888     std::vector<std::string> options, options_to_filter;
00889 
00890     std::ostringstream heading;
00891     heading << HEADING_PREFIX << COLUMN_SEPARATOR << _("Type")
00892         << COLUMN_SEPARATOR << _("Name")
00893         << COLUMN_SEPARATOR << _("Level^Lvl.")
00894         << COLUMN_SEPARATOR << _("XP");
00895     heading << COLUMN_SEPARATOR << _("Traits");
00896 
00897     gui::menu::basic_sorter sorter;
00898     sorter.set_alpha_sort(1).set_alpha_sort(2);
00899     sorter.set_level_sort(3,4).set_xp_sort(4).set_alpha_sort(5);
00900 
00901     options.push_back(heading.str());
00902     options_to_filter.push_back(options.back());
00903 
00904     foreach (const unit* u, recall_list_team)
00905     {
00906         std::stringstream option, option_to_filter;
00907         std::string name = u->name();
00908         if (name.empty()) name = "-";
00909 
00910         option << IMAGE_PREFIX << u->absolute_image();
00911     #ifndef LOW_MEM
00912         option << "~RC("  << u->team_color() << '>'
00913             << team::get_side_color_index(side_num) << ')';
00914     #endif
00915         option << COLUMN_SEPARATOR
00916             << u->type_name() << COLUMN_SEPARATOR
00917             << name << COLUMN_SEPARATOR;
00918 
00919         // Show units of level (0=gray, 1 normal, 2 bold, 2+ bold&wbright)
00920         const int level = u->level();
00921         if(level < 1) {
00922             option << "<150,150,150>";
00923         } else if(level == 1) {
00924             option << font::NORMAL_TEXT;
00925         } else if(level == 2) {
00926             option << font::BOLD_TEXT;
00927         } else if(level > 2 ) {
00928             option << font::BOLD_TEXT << "<255,255,255>";
00929         }
00930         option << level << COLUMN_SEPARATOR;
00931 
00932         option << font::color2markup(u->xp_color()) << u->experience() << "/";
00933         if (u->can_advance())
00934             option << u->max_experience();
00935         else
00936             option << "-";
00937 
00938         option_to_filter << u->type_name() << " " << name << " " << u->level();
00939 
00940         option << COLUMN_SEPARATOR;
00941         foreach (const t_string& trait, u->trait_names()) {
00942             option << trait << '\n';
00943             option_to_filter << " " << trait;
00944         }
00945 
00946         options.push_back(option.str());
00947         options_to_filter.push_back(option_to_filter.str());
00948     }
00949 
00950     int res = 0;
00951 
00952     {
00953         gui::dialog rmenu(*gui_, _("Recall") + get_title_suffix(side_num),
00954             _("Select unit:") + std::string("\n"),
00955             gui::OK_CANCEL, gui::dialog::default_style);
00956         rmenu.set_menu(options, &sorter);
00957 
00958         gui::filter_textbox* filter = new gui::filter_textbox(gui_->video(),
00959             _("Filter: "), options, options_to_filter, 1, rmenu, 200);
00960         rmenu.set_textbox(filter);
00961 
00962         delete_recall_unit recall_deleter(*gui_, *filter, recall_list_team);
00963         gui::dialog_button_info delete_button(&recall_deleter,_("Dismiss Unit"));
00964         rmenu.add_button(delete_button);
00965 
00966         rmenu.add_button(new help::help_button(*gui_,"recruit_and_recall"),
00967             gui::dialog::BUTTON_HELP);
00968 
00969         dialogs::units_list_preview_pane unit_preview(recall_list_team, filter);
00970         rmenu.add_pane(&unit_preview);
00971 
00972         //sort by level
00973         static int sort_by = 3;
00974         static bool sort_reversed = false;
00975 
00976         if(sort_by >= 0) {
00977             rmenu.get_menu().sort_by(sort_by);
00978             // "reclick" on the sorter to reverse the order
00979             if(sort_reversed) {
00980                 rmenu.get_menu().sort_by(sort_by);
00981             }
00982         }
00983 
00984         res = rmenu.show();
00985         res = filter->get_index(res);
00986 
00987         sort_by = rmenu.get_menu().get_sort_by();
00988         sort_reversed = rmenu.get_menu().get_sort_reversed();
00989 
00990         if (res < 0) return;
00991 
00992     }
00993 
00994     int wb_gold = resources::whiteboard->get_spent_gold_for(side_num);
00995     if (current_team.gold() - wb_gold < current_team.recall_cost()) {
00996         utils::string_map i18n_symbols;
00997         i18n_symbols["cost"] = lexical_cast<std::string>(current_team.recall_cost());
00998         std::string msg = vngettext(
00999             "You must have at least 1 gold piece to recall a unit",
01000             "You must have at least $cost gold pieces to recall a unit",
01001             current_team.recall_cost(), i18n_symbols);
01002         gui2::show_transient_message(gui_->video(), "", msg);
01003         return;
01004     }
01005 
01006     LOG_NG << "recall index: " << res << "\n";
01007     const events::command_disabler disable_commands;
01008 
01009     map_location recall_location = last_hex;
01010     map_location recall_from = map_location::null_location;
01011     std::string err;
01012     { wb::future_map_if_active future; // future unit map removes invisible units from map, don't do this outside of planning mode
01013         err = find_recall_location(side_num, recall_location, recall_from, *(recall_list_team[res]));
01014     } // end planned unit map scope
01015     if(!err.empty()) {
01016         gui2::show_transient_message(gui_->video(), "", err);
01017         return;
01018     }
01019     unit* recalled_unit;
01020     recalled_unit = new unit(*(recall_list_team[res]));
01021 
01022     if (!resources::whiteboard->save_recall(*recalled_unit, side_num, recall_location)) {
01023         do_recall(*recalled_unit, side_num, recall_location, recall_from);
01024     }
01025 }
01026 
01027 bool menu_handler::do_recall(const unit& un, int side_num, const map_location& recall_location, const map_location& recall_from)
01028 {
01029     team &current_team = teams_[side_num - 1];
01030     std::vector<unit>& recall_list_team = current_team.recall_list();
01031 
01032     std::vector<unit>::iterator it = find_if_matches_id(recall_list_team, un.id());
01033     if (it == recall_list_team.end())
01034     {
01035         ERR_NG << "menu_handler::do_recall(): Unit doesn't exist in recall list.\n";
01036         return false;
01037     }
01038 
01039     recall_list_team.erase(it);
01040     recorder.add_recall(un.id(), recall_location, recall_from);
01041     place_recruit(un, recall_location, recall_from, true, true);
01042     statistics::recall_unit(un);
01043     current_team.spend_gold(current_team.recall_cost());
01044 
01045     bool shroud_cleared = clear_shroud(side_num);
01046     if (shroud_cleared) {
01047         clear_undo_stack(side_num);
01048     } else {
01049         resources::undo_stack->push_back(undo_action(un, recall_location, recall_from, undo_action::RECALL));
01050     }
01051 
01052     resources::redo_stack->clear();
01053     gui_->redraw_minimap();
01054     gui_->invalidate_game_status();
01055     gui_->invalidate(recall_location);
01056     recorder.add_checksum_check(recall_location);
01057     return true;
01058 }
01059 
01060 void menu_handler::undo(int side_num)
01061 {
01062     if(resources::undo_stack->empty())
01063         return;
01064 
01065     const events::command_disabler disable_commands;
01066     team &current_team = teams_[side_num - 1];
01067 
01068     // Get the action to undo. (This will be placed on the redo stack, but
01069     // only if the undo is successful.)
01070     undo_action action = resources::undo_stack->back();
01071     resources::undo_stack->pop_back();
01072 
01073     if (action.is_dismiss()) {
01074         //undo a dismissal
01075 
01076         if(!current_team.persistent()) {
01077             ERR_NG << "trying to undo a dismissal for side " << side_num
01078                 << ", which has no recall list!\n";
01079             return;
01080         }
01081         current_team.recall_list().push_back(action.affected_unit);
01082         resources::whiteboard->on_gamestate_change();
01083     } else if(action.is_recall()) {
01084 
01085         if(!current_team.persistent()) {
01086             ERR_NG << "trying to undo a recall for side " << side_num
01087                 << ", which has no recall list!\n";
01088             return;
01089         }
01090         // Undo a recall action
01091         if(units_.count(action.recall_loc) == 0) {
01092             return;
01093         }
01094 
01095         const unit &un = *units_.find(action.recall_loc);
01096         statistics::un_recall_unit(un);
01097         current_team.spend_gold(-current_team.recall_cost());
01098 
01099         current_team.recall_list().push_back(un);
01100         // invalidate before erasing allow us
01101         // to also do the overlapped hexes
01102         gui_->invalidate(action.recall_loc);
01103         units_.erase(action.recall_loc);
01104         resources::whiteboard->on_gamestate_change();
01105     } else if(action.is_recruit()) {
01106         // Undo a recruit action
01107         if(units_.count(action.recall_loc) == 0) {
01108             return;
01109         }
01110 
01111         const unit &un = *units_.find(action.recall_loc);
01112         statistics::un_recruit_unit(un);
01113         assert(un.type());
01114         current_team.spend_gold(-un.type()->cost());
01115 
01116         //MP_COUNTDOWN take away recruit bonus
01117         if(action.countdown_time_bonus)
01118         {
01119             current_team.set_action_bonus_count(current_team.action_bonus_count() - 1);
01120         }
01121 
01122         // invalidate before erasing allow us
01123         // to also do the ovelerlapped hexes
01124         gui_->invalidate(action.recall_loc);
01125         units_.erase(action.recall_loc);
01126         resources::whiteboard->on_gamestate_change();
01127     } else {
01128         // Undo a move action
01129         const int starting_moves = action.starting_moves;
01130         std::vector<map_location> route = action.route;
01131         std::reverse(route.begin(),route.end());
01132         unit_map::iterator u = units_.find(route.front());
01133         const unit_map::iterator u_end = units_.find(route.back());
01134         if(u == units_.end() || u_end != units_.end()) {
01135             //this can actually happen if the scenario designer has abused the [allow_undo] command
01136             ERR_NG << "Illegal 'undo' found. Possible abuse of [allow_undo]?\n";
01137             return;
01138         }
01139 
01140         if(map_.is_village(route.front())) {
01141             get_village(route.front(), action.original_village_owner + 1);
01142             //MP_COUNTDOWN take away capture bonus
01143             if(action.countdown_time_bonus)
01144             {
01145                 current_team.set_action_bonus_count(current_team.action_bonus_count() - 1);
01146             }
01147         }
01148 
01149         action.starting_moves = u->movement_left();
01150 
01151         undo_action action_copy(action);
01152 
01153         unit_display::move_unit(route, *u, teams_, true, action_copy.starting_dir);
01154 
01155         units_.move(u->get_location(), route.back());
01156         unit::clear_status_caches();
01157 
01158         u = units_.find(route.back());
01159         u->set_goto(map_location());
01160         u->set_movement(starting_moves);
01161         u->set_standing();
01162 
01163         gui_->invalidate_unit_after_move(route.front(), route.back());
01164         resources::whiteboard->on_gamestate_change();
01165     }
01166     recorder.undo();
01167     resources::redo_stack->push_back(action);
01168 
01169     gui_->invalidate_unit();
01170     gui_->invalidate_game_status();
01171     gui_->redraw_minimap();
01172     gui_->draw();
01173 }
01174 
01175 void menu_handler::redo(int side_num)
01176 {
01177     if(resources::redo_stack->empty())
01178         return;
01179 
01180     const events::command_disabler disable_commands;
01181     team &current_team = teams_[side_num - 1];
01182 
01183     // Get the action to undo. (This will be placed on the undo stack, but
01184     // only if the redo is successful.)
01185     undo_action action = resources::redo_stack->back();
01186     resources::redo_stack->pop_back();
01187 
01188     if (action.is_dismiss()) {
01189         if(!current_team.persistent()) {
01190             ERR_NG << "trying to redo a dismiss for side " << side_num
01191                 << ", which has no recall list!\n";
01192             return;
01193         }
01194         //redo a dismissal
01195         recorder.add_disband(action.affected_unit.id());
01196         std::vector<unit>::iterator unit_it =
01197             find_if_matches_id(current_team.recall_list(), action.affected_unit.id());
01198         current_team.recall_list().erase(unit_it);
01199         resources::whiteboard->on_gamestate_change();
01200     } else if(action.is_recall()) {
01201         if(!current_team.persistent()) {
01202             ERR_NG << "trying to redo a recall for side " << side_num
01203                 << ", which has no recall list!\n";
01204             return;
01205         }
01206         // Redo recall
01207 
01208         recorder.add_recall(action.affected_unit.id(), action.recall_loc, action.recall_from);
01209         map_location loc = action.recall_loc;
01210         map_location from = map_location::null_location;
01211         const events::command_disabler disable_commands;
01212         const std::string &msg = find_recall_location(side_num, loc, from, action.affected_unit);
01213         if(msg.empty()) {
01214             unit un = action.affected_unit;
01215             //remove the unit from the recall list
01216             std::vector<unit>::iterator unit_it =
01217                 find_if_matches_id(current_team.recall_list(), action.affected_unit.id());
01218             assert(unit_it != current_team.recall_list().end());
01219             current_team.recall_list().erase(unit_it);
01220 
01221             place_recruit(un, loc, from, true, true);
01222             statistics::recall_unit(un);
01223             current_team.spend_gold(current_team.recall_cost());
01224             gui_->invalidate(loc);
01225             recorder.add_checksum_check(loc);
01226         } else {
01227             recorder.undo();
01228             gui::dialog(*gui_,"",msg,gui::OK_ONLY).show();
01229             return;
01230         }
01231         resources::whiteboard->on_gamestate_change();
01232     } else if(action.is_recruit()) {
01233         // Redo recruit action
01234         map_location loc = action.recall_loc;
01235         map_location from = action.recall_from;
01236         const std::string name = action.affected_unit.type_id();
01237 
01238         //search for the unit to be recruited in recruits
01239         int recruit_num = 0;
01240         const std::set<std::string>& recruits = current_team.recruits();
01241         for(std::set<std::string>::const_iterator r = recruits.begin(); ; ++r) {
01242             if (r == recruits.end()) {
01243                 ERR_NG << "trying to redo a recruit for side " << side_num
01244                     << ", which does not recruit type \"" << name << "\"\n";
01245                 assert(false);
01246                 return;
01247             }
01248             if (name == *r) {
01249                 break;
01250             }
01251             ++recruit_num;
01252         }
01253         last_recruit_ = name;
01254         recorder.add_recruit(recruit_num,loc,from);
01255         const events::command_disabler disable_commands;
01256         const std::string &msg = find_recruit_location(side_num, loc, from, action.affected_unit.type_id());
01257         if(msg.empty()) {
01258             const unit new_unit = action.affected_unit;
01259             //unit new_unit(action.affected_unit.type(),team_num_,true);
01260             place_recruit(new_unit, loc, from, false, true);
01261             current_team.spend_gold(new_unit.type()->cost());
01262             statistics::recruit_unit(new_unit);
01263             gui_->invalidate(loc);
01264 
01265             //MP_COUNTDOWN: restore recruitment bonus
01266             current_team.set_action_bonus_count(1 + current_team.action_bonus_count());
01267 
01268             recorder.add_checksum_check(loc);
01269         } else {
01270             recorder.undo();
01271             gui::dialog(*gui_,"",msg,gui::OK_ONLY).show();
01272             return;
01273         }
01274         resources::whiteboard->on_gamestate_change();
01275     } else {
01276         // Redo movement action
01277         const int starting_moves = action.starting_moves;
01278         std::vector<map_location> route = action.route;
01279         unit_map::iterator u = units_.find(route.front());
01280         if(u == units_.end()) {
01281             ERR_NG << "Illegal movement 'redo'.\n";
01282             assert(false);
01283             return;
01284         }
01285 
01286         action.starting_moves = u->movement_left();
01287 
01288         undo_action action_copy(action);
01289 
01290         unit_display::move_unit(route, *u, teams_);
01291 
01292         units_.move(u->get_location(), route.back());
01293         u = units_.find(route.back());
01294 
01295         unit::clear_status_caches();
01296         u->set_goto(action_copy.affected_unit.get_goto());
01297         u->set_movement(starting_moves);
01298         u->set_standing();
01299 
01300         if(map_.is_village(route.back())) {
01301             get_village(route.back(), u->side());
01302             //MP_COUNTDOWN restore capture bonus
01303             if(action_copy.countdown_time_bonus)
01304             {
01305                 current_team.set_action_bonus_count(1 + current_team.action_bonus_count());
01306             }
01307         }
01308 
01309         gui_->invalidate_unit_after_move(route.front(), route.back());
01310         resources::whiteboard->on_gamestate_change();
01311 
01312         recorder.add_movement(action_copy.route);
01313     }
01314     resources::undo_stack->push_back(action);
01315 
01316     gui_->invalidate_unit();
01317     gui_->invalidate_game_status();
01318     gui_->redraw_minimap();
01319     gui_->draw();
01320 }
01321 
01322 bool menu_handler::clear_shroud(int side_num)
01323 {
01324     bool cleared = teams_[side_num - 1].auto_shroud_updates() &&
01325 		::clear_shroud(side_num);
01326     return cleared;
01327 }
01328 
01329 void menu_handler::clear_undo_stack(int side_num)
01330 {
01331     if (!teams_[side_num - 1].auto_shroud_updates())
01332         apply_shroud_changes(*resources::undo_stack, side_num);
01333     resources::undo_stack->clear();
01334 }
01335 
01336 // Highlights squares that an enemy could move to on their turn, showing how many can reach each square.
01337 void menu_handler::show_enemy_moves(bool ignore_units, int side_num)
01338 {
01339     wb::future_map future; // use unit positions as if all planned actions were executed
01340 
01341     gui_->unhighlight_reach();
01342 
01343     // Compute enemy movement positions
01344     for(unit_map::iterator u = units_.begin(); u != units_.end(); ++u) {
01345         bool invisible = u->invisible(u->get_location());
01346 
01347         if (teams_[side_num - 1].is_enemy(u->side()) &&
01348             !gui_->fogged(u->get_location()) && !u->incapacitated() && !invisible)
01349         {
01350             const unit_movement_resetter move_reset(*u);
01351             const pathfind::paths& path = pathfind::paths(map_,units_,
01352                 *u, teams_, false, true,
01353                 teams_[gui_->viewing_team()], 0, false, ignore_units);
01354 
01355             gui_->highlight_another_reach(path);
01356         }
01357     }
01358 }
01359 
01360 void menu_handler::toggle_shroud_updates(int side_num)
01361 {
01362     team &current_team = teams_[side_num - 1];
01363     bool auto_shroud = current_team.auto_shroud_updates();
01364     // If we're turning automatic shroud updates on, then commit all moves
01365     if (!auto_shroud) update_shroud_now(side_num);
01366     current_team.set_auto_shroud_updates(!auto_shroud);
01367 }
01368 
01369 void menu_handler::update_shroud_now(int side_num)
01370 {
01371     clear_undo_stack(side_num);
01372 }
01373 
01374 bool menu_handler::end_turn(int side_num)
01375 {
01376     if(!resources::state_of_game->allow_end_turn()) {
01377         gui2::show_message((*gui_).video(), "", _("You cannot end your turn yet!"), gui2::tmessage::ok_button);
01378         return false;
01379     }
01380 
01381     bool unmoved_units = false, partmoved_units = false, some_units_have_moved = false;
01382     int units_alive = 0;
01383     for(unit_map::const_iterator un = units_.begin(); un != units_.end(); ++un) {
01384         if (un->side() == side_num) {
01385             units_alive++;
01386             // @todo whiteboard should take into consideration units that have a planned move but
01387             // can still plan more movement in the same turn
01388             if (unit_can_move(*un) && !resources::whiteboard->unit_has_actions(&*un)) {
01389                 if (!un->has_moved()) {
01390                     unmoved_units = true;
01391                 }
01392 
01393                 partmoved_units = true;
01394             }
01395             if (un->has_moved()) {
01396                 some_units_have_moved = true;
01397             }
01398         }
01399     }
01400 
01401     //Ask for confirmation if the player hasn't made any moves (other than gotos).
01402     if (preferences::confirm_no_moves() && units_alive && !some_units_have_moved
01403             && !resources::whiteboard->current_side_has_actions()) {
01404         const int res = gui2::show_message((*gui_).video(), "", _("You have not started your turn yet. Do you really want to end your turn?"), gui2::tmessage::yes_no_buttons);
01405         if(res == gui2::twindow::CANCEL) {
01406             return false;
01407         }
01408     }
01409 
01410     // Ask for confirmation if units still have movement left
01411     if(preferences::yellow_confirm() && partmoved_units) {
01412         const int res = gui2::show_message((*gui_).video(), "", _("Some units have movement left. Do you really want to end your turn?"), gui2::tmessage::yes_no_buttons);
01413         if(res == gui2::twindow::CANCEL) {
01414             return false;
01415         }
01416     } else if (preferences::green_confirm() && unmoved_units) {
01417         const int res = gui2::show_message((*gui_).video(), "", _("Some units have movement left. Do you really want to end your turn?"), gui2::tmessage::yes_no_buttons);
01418         if(res == gui2::twindow::CANCEL) {
01419             return false;
01420         }
01421     }
01422 
01423     // Auto-execute remaining whiteboard planned actions
01424     // Only finish turn if they all execute successfully, i.e. no ambush, etc.
01425     if (!resources::whiteboard->allow_end_turn()) {
01426         return false;
01427     }
01428 
01429     return true;
01430 }
01431 
01432 void menu_handler::goto_leader(int side_num)
01433 {
01434     unit_map::const_iterator i = units_.find_leader(side_num);
01435     if(i != units_.end()) {
01436         clear_shroud(side_num);
01437         gui_->scroll_to_tile(i->get_location(), game_display::WARP);
01438     }
01439 }
01440 
01441 void menu_handler::unit_description()
01442 {
01443     const unit_map::const_iterator un = current_unit();
01444     if(un != units_.end()) {
01445         dialogs::show_unit_description(*un);
01446     }
01447 }
01448 
01449 void menu_handler::rename_unit()
01450 {
01451     const unit_map::iterator un = current_unit();
01452     if (un == units_.end() || gui_->viewing_side() != un->side())
01453         return;
01454     if (un->unrenamable())
01455         return;
01456 
01457     std::string name = un->name();
01458     const int res = gui::show_dialog(*gui_,NULL,_("Rename Unit"),"", gui::OK_CANCEL,NULL,NULL,"",&name);
01459     if(res == 0) {
01460         recorder.add_rename(name, un->get_location());
01461         un->rename(name);
01462         gui_->invalidate_unit();
01463     }
01464 }
01465 
01466 unit_map::iterator menu_handler::current_unit()
01467 {
01468     const mouse_handler& mousehandler = resources::controller->get_mouse_handler_base();
01469 
01470     unit_map::iterator res = find_visible_unit(mousehandler.get_last_hex(),
01471         teams_[gui_->viewing_team()]);
01472     if(res != units_.end()) {
01473         return res;
01474     } else {
01475         return find_visible_unit(mousehandler.get_selected_hex(),
01476             teams_[gui_->viewing_team()]);
01477     }
01478 }
01479 
01480 void menu_handler::create_unit_2(mouse_handler& mousehandler)
01481 {
01482     assert(gui_ != NULL);
01483     //
01484     // The unit creation dialog makes sure unit types
01485     // are properly cached.
01486     //
01487     gui2::tunit_create create_dlg;
01488     create_dlg.show(gui_->video());
01489 
01490     if(create_dlg.no_choice()) {
01491         return;
01492     }
01493 
01494     const std::string& ut_id = create_dlg.choice();
01495     const unit_type *utp = unit_types.find(ut_id);
01496     if (!utp) {
01497         ERR_NG << "create unit dialog returned inexistent or unusable unit_type id '" << ut_id << "'\n";
01498         return;
01499     }
01500 
01501     const unit_type &ut = *utp;
01502 
01503     unit_race::GENDER gender = create_dlg.gender();
01504 
01505     // Do not try to set bad genders, may mess up l10n
01506     // FIXME: is this actually necessary?
01507     if(ut.genders().end() == std::find(ut.genders().begin(), ut.genders().end(), gender)) {
01508         gender = ut.genders().front();
01509     }
01510 
01511     unit chosen(&ut, 1, true, gender);
01512     chosen.new_turn();
01513 
01514     const map_location& loc = mousehandler.get_last_hex();
01515     units_.replace(loc, chosen);
01516 
01517     if(map_.is_village(loc)) {
01518         get_village(loc, chosen.side());
01519     }
01520 
01521     gui_->invalidate(loc);
01522     gui_->invalidate_unit();
01523 }
01524 
01525 void menu_handler::create_unit(mouse_handler& mousehandler)
01526 {
01527     /** @todo reenable after releasing 1.7.4; as-is causes memory corruption */
01528     if(gui2::new_widgets) {
01529         create_unit_2(mousehandler);
01530         return;
01531     }
01532 
01533     std::vector<std::string> options;
01534     static int last_selection = -1;
01535     static bool random_gender = false;
01536     std::vector<const unit_type*> unit_choices;
01537     const std::string heading = std::string(1,HEADING_PREFIX) +
01538                                 _("Race")      + COLUMN_SEPARATOR +
01539                                 _("Type");
01540     options.push_back(heading);
01541 
01542     foreach (const unit_type_data::unit_type_map::value_type &i, unit_types.types())
01543     {
01544         std::stringstream row;
01545 
01546         unit_types.find(i.first, unit_type::HELP_INDEX);
01547 
01548         std::string race;
01549         if (const unit_race *r = unit_types.find_race(i.second.race())) {
01550             race = r->plural_name();
01551         }
01552         row << race << COLUMN_SEPARATOR;
01553         row << i.second.type_name() << COLUMN_SEPARATOR;
01554 
01555         options.push_back(row.str());
01556         unit_choices.push_back(&i.second);
01557     }
01558 
01559     int choice = 0;
01560     bool random_gender_choice = random_gender;
01561     {
01562         gui::dialog umenu(*gui_, _("Create Unit (Debug!)"), "", gui::OK_CANCEL);
01563 
01564         umenu.add_option(
01565             (formatter()<<_("Gender: ")<<_("gender^Random")).str(),
01566             random_gender_choice,
01567             gui::dialog::BUTTON_EXTRA
01568         );
01569 
01570         gui::menu::basic_sorter sorter;
01571         sorter.set_alpha_sort(0).set_alpha_sort(1);
01572         umenu.set_menu(options, &sorter);
01573 
01574         gui::filter_textbox* filter = new gui::filter_textbox(gui_->video(),
01575             _("Filter: "), options, options, 1, umenu, 200);
01576         umenu.set_textbox(filter);
01577 
01578         //sort by race then by type name
01579         umenu.get_menu().sort_by(1);
01580         umenu.get_menu().sort_by(0);
01581         if (last_selection >= 0)
01582             umenu.get_menu().move_selection(last_selection);
01583         else
01584             umenu.get_menu().reset_selection();
01585 
01586         dialogs::unit_types_preview_pane unit_preview(unit_choices, filter, 1, dialogs::unit_types_preview_pane::SHOW_ALL);
01587         umenu.add_pane(&unit_preview);
01588         unit_preview.set_selection(umenu.get_menu().selection());
01589 
01590         choice = umenu.show();
01591         choice = filter->get_index(choice);
01592         random_gender_choice = umenu.option_checked(0);
01593     }
01594 
01595     if (size_t(choice) < unit_choices.size()) {
01596         last_selection = choice;
01597         random_gender  = random_gender_choice;
01598 
01599         const unit_type* type = unit_choices[choice];
01600         const unit_race::GENDER gender = random_gender ? unit_race::NUM_GENDERS :
01601             type->genders().empty() ? unit_race::MALE : type->genders().front();
01602 
01603         unit chosen(type, 1, true, gender);
01604         chosen.new_turn();
01605 
01606         const map_location loc = mousehandler.get_last_hex();
01607         units_.replace(loc, chosen);
01608         unit_display::unit_recruited(loc);
01609 
01610         if(map_.is_village(loc)) {
01611             get_village(loc, chosen.side());
01612         }
01613 
01614         gui_->invalidate(loc);
01615         gui_->invalidate_unit();
01616     }
01617 }
01618 
01619 void menu_handler::change_side(mouse_handler& mousehandler)
01620 {
01621     const map_location& loc = mousehandler.get_last_hex();
01622     const unit_map::iterator i = units_.find(loc);
01623     if(i == units_.end()) {
01624         if(!map_.is_village(loc))
01625             return;
01626 
01627         // village_owner returns -1 for free village, so team 0 will get it
01628         int team = village_owner(loc, teams_) + 1;
01629         // team is 0-based so team=team::nteams() is not a team
01630         // but this will make get_village free it
01631         if(team > team::nteams()) {
01632             team = 0;
01633         }
01634         get_village(loc, team + 1);
01635     } else {
01636         int side = i->side();
01637         ++side;
01638         if(side > team::nteams()) {
01639             side = 1;
01640         }
01641         i->set_side(side);
01642 
01643         if(map_.is_village(loc)) {
01644             get_village(loc, side);
01645         }
01646     }
01647 }
01648 
01649 void menu_handler::label_terrain(mouse_handler& mousehandler, bool team_only)
01650 {
01651     const map_location& loc = mousehandler.get_last_hex();
01652     if (map_.on_board(loc) == false) {
01653         return;
01654     }
01655 
01656     const terrain_label* old_label = gui_->labels().get_label(loc);
01657     std::string label = old_label ? old_label->text() : "";
01658 
01659     if(gui2::tedit_label::execute(label, team_only, gui_->video())) {
01660         std::string team_name;
01661         SDL_Color color = font::LABEL_COLOR;
01662 
01663         if (team_only) {
01664             team_name = gui_->labels().team_name();
01665         } else {
01666             color = int_to_color(team::get_side_rgb(gui_->viewing_side()));
01667         }
01668         const std::string& old_team_name = old_label ? old_label->team_name() : "";
01669         // remove the old label if we changed the team_name
01670         if (team_only == (old_team_name == "")) {
01671             const terrain_label* old = gui_->labels().set_label(loc, "", old_team_name, color);
01672             if (old) recorder.add_label(old);
01673         }
01674         const terrain_label* res = gui_->labels().set_label(loc, label, team_name, color);
01675         if (res)
01676             recorder.add_label(res);
01677     }
01678 }
01679 
01680 void menu_handler::clear_labels()
01681 {
01682     if (gui_->team_valid()
01683        && !is_observer())
01684     {
01685         gui_->labels().clear(gui_->current_team_name(), false);
01686         recorder.clear_labels(gui_->current_team_name(), false);
01687     }
01688 }
01689 
01690 void menu_handler::continue_move(mouse_handler &mousehandler, int side_num)
01691 {
01692     unit_map::iterator i = current_unit();
01693     if (i == units_.end() || !i->move_interrupted()) {
01694         i = units_.find(mousehandler.get_selected_hex());
01695         if (i == units_.end() || !i->move_interrupted()) return;
01696     }
01697     move_unit_to_loc(i, i->get_interrupted_move(), true,
01698         side_num, mousehandler);
01699 }
01700 
01701 void menu_handler::move_unit_to_loc(const unit_map::iterator &ui,
01702     const map_location& target, bool continue_move, int side_num,
01703     mouse_handler &mousehandler)
01704 {
01705     assert(ui != units_.end());
01706 
01707     pathfind::marked_route route = mousehandler.get_route(&*ui, target, teams_[side_num - 1]);
01708 
01709     if(route.steps.empty())
01710         return;
01711 
01712     assert(route.steps.front() == ui->get_location());
01713 
01714     gui_->set_route(&route);
01715     move_unit(NULL, route.steps, &recorder, resources::undo_stack, true, NULL, continue_move);
01716     gui_->invalidate_game_status();
01717 }
01718 
01719 void menu_handler::execute_gotos(mouse_handler &mousehandler, int side)
01720 {
01721     // we will loop on all gotos and try to fully move a maximum of them,
01722     // but we want to avoid multiple blocking of the same unit,
01723     // so, if possible, it's better to first wait that the blocker move
01724 
01725     bool wait_blocker_move = true;
01726     std::set<map_location> fully_moved;
01727 
01728     bool change = false;
01729     bool blocked_unit = false;
01730     do {
01731         change = false;
01732         blocked_unit = false;
01733         for(unit_map::iterator ui = units_.begin(); ui != units_.end(); ++ui) {
01734             if (ui->side() != side  || ui->movement_left() == 0)
01735                 continue;
01736 
01737             const map_location &current_loc = ui->get_location();
01738             const map_location &goto_loc = ui->get_goto();
01739 
01740             if(goto_loc == current_loc){
01741                 ui->set_goto(map_location());
01742                 continue;
01743             }
01744 
01745             if(!map_.on_board(goto_loc))
01746                 continue;
01747 
01748             // avoid pathfinding calls for finished units
01749             if(fully_moved.count(current_loc))
01750                 continue;
01751 
01752             pathfind::marked_route route = mousehandler.get_route(&*ui, goto_loc, teams_[side - 1]);
01753 
01754             if(route.steps.size() <= 1) { // invalid path
01755                 fully_moved.insert(current_loc);
01756                 continue;
01757             }
01758 
01759             // look where we will stop this turn (turn_1 waypoint or goto)
01760             map_location next_stop = goto_loc;
01761             pathfind::marked_route::mark_map::const_iterator w = route.marks.begin();
01762             for(; w != route.marks.end(); ++w) {
01763                 if (w->second.turns == 1) {
01764                     next_stop = w->first;
01765                     break;
01766                 }
01767             }
01768 
01769             if(next_stop == current_loc) {
01770                 fully_moved.insert(current_loc);
01771                 continue;
01772             }
01773 
01774             // we delay each blocked move because some other change
01775             // may open a another not blocked path
01776             if(units_.count(next_stop)) {
01777                 blocked_unit = true;
01778                 if (wait_blocker_move)
01779                     continue;
01780             }
01781 
01782             gui_->set_route(&route);
01783             int moves = ::move_unit(NULL, route.steps, &recorder, resources::undo_stack, true, NULL, false);
01784             change = moves > 0;
01785 
01786             if (change) {
01787                 // something changed, resume waiting blocker (maybe one can move now)
01788                 wait_blocker_move = true;
01789             }
01790         }
01791 
01792         if(!change && wait_blocker_move) {
01793             // no change when waiting, stop waiting and retry
01794             wait_blocker_move = false;
01795             change = true;
01796         }
01797     } while(change && blocked_unit);
01798 
01799     // erase the footsteps after movement
01800     gui_->set_route(NULL);
01801     gui_->invalidate_game_status();
01802 }
01803 
01804 
01805 void menu_handler::toggle_ellipses()
01806 {
01807     preferences::set_ellipses(!preferences::ellipses());
01808     gui_->invalidate_all();
01809 }
01810 
01811 void menu_handler::toggle_grid()
01812 {
01813     preferences::set_grid(!preferences::grid());
01814     gui_->invalidate_all();
01815 }
01816 
01817 void menu_handler::unit_hold_position(mouse_handler &mousehandler, int side_num)
01818 {
01819     const unit_map::iterator un = units_.find(mousehandler.get_selected_hex());
01820     if (un != units_.end() && un->side() == side_num && un->movement_left() >= 0)
01821     {
01822         un->set_hold_position(!un->hold_position());
01823         gui_->invalidate(mousehandler.get_selected_hex());
01824 
01825         mousehandler.set_current_paths(pathfind::paths());
01826         gui_->draw();
01827 
01828         if (un->hold_position()) {
01829             un->set_user_end_turn(true);
01830             mousehandler.cycle_units(false);
01831         }
01832     }
01833 }
01834 
01835 void menu_handler::end_unit_turn(mouse_handler &mousehandler, int side_num)
01836 {
01837     const unit_map::iterator un = units_.find(mousehandler.get_selected_hex());
01838     if (un != units_.end() && un->side() == side_num && un->movement_left() >= 0)
01839     {
01840         un->set_user_end_turn(!un->user_end_turn());
01841         if (un->hold_position() && !un->user_end_turn()) {
01842             un->set_hold_position(false);
01843         }
01844         gui_->invalidate(mousehandler.get_selected_hex());
01845 
01846         mousehandler.set_current_paths(pathfind::paths());
01847         gui_->draw();
01848 
01849         if (un->user_end_turn()) {
01850             mousehandler.cycle_units(false);
01851         }
01852     }
01853 }
01854 
01855 void menu_handler::search()
01856 {
01857     std::ostringstream msg;
01858     msg << _("Search");
01859     if(last_search_hit_.valid()) {
01860         msg << " [" << last_search_ << "]";
01861     }
01862     msg << ':';
01863     textbox_info_.show(gui::TEXTBOX_SEARCH,msg.str(), "", false, *gui_);
01864 }
01865 
01866 void menu_handler::do_speak(){
01867     //None of the two parameters really needs to be passed since the information belong to members of the class.
01868     //But since it makes the called method more generic, it is done anyway.
01869     chat_handler::do_speak(textbox_info_.box()->text(),textbox_info_.check() != NULL ? textbox_info_.check()->checked() : false);
01870 }
01871 
01872 
01873 void menu_handler::add_chat_message(const time_t& time,
01874         const std::string& speaker, int side, const std::string& message,
01875         events::chat_handler::MESSAGE_TYPE type)
01876 {
01877     gui_->add_chat_message(time, speaker, side, message, type, false);
01878 }
01879 
01880 //simple command args parser, separated from command_handler for clarity.
01881 //a word begins with a nonspace
01882 //n-th arg is n-th word up to the next space
01883 //n-th data is n-th word up to the end
01884 //cmd is 0-th arg, begins at 0 always.
01885 class cmd_arg_parser
01886 {
01887     public:
01888         cmd_arg_parser() :
01889             str_(""),
01890             args(1, 0),
01891             args_end(false)
01892         {
01893         }
01894 
01895         explicit cmd_arg_parser(const std::string& str) :
01896             str_(str),
01897             args(1, 0),
01898             args_end(false)
01899         {
01900         }
01901 
01902         void parse(const std::string& str)
01903         {
01904             str_ = str;
01905             args.clear();
01906             args.push_back(0);
01907             args_end = false;
01908         }
01909 
01910         const std::string& get_str() const
01911         {
01912             return str_;
01913         }
01914         std::string get_arg(unsigned n) const
01915         {
01916             advance_to_arg(n);
01917             if (n < args.size()) {
01918                 return std::string(str_, args[n], str_.find(' ', args[n]) - args[n]);
01919             } else {
01920                 return "";
01921             }
01922         }
01923         std::string get_data(unsigned n) const
01924         {
01925             advance_to_arg(n);
01926             if (n < args.size()) {
01927                 std::string data(str_, args[n]);
01928                 return utils::strip(data);
01929             } else {
01930                 return "";
01931             }
01932         }
01933         std::string get_cmd() const
01934         {
01935             return get_arg(0);
01936         }
01937     private:
01938         cmd_arg_parser& operator=(const cmd_arg_parser&);
01939         cmd_arg_parser(const cmd_arg_parser&);
01940         void advance_to_arg(unsigned n) const
01941         {
01942             while (n < args.size() && !args_end) {
01943                 size_t first_space = str_.find_first_of(' ', args.back());
01944                 size_t next_arg_begin = str_.find_first_not_of(' ', first_space);
01945                 if (next_arg_begin != std::string::npos) {
01946                     args.push_back(next_arg_begin);
01947                 } else {
01948                     args_end = true;
01949                 }
01950             }
01951         }
01952         std::string str_;
01953         mutable std::vector<size_t> args;
01954         mutable bool args_end;
01955 };
01956 
01957 //A helper class template with a slim public interface
01958 //This represents a map of strings to void()-member-function-of-Worker-pointers
01959 //with all the common functionality like general help, command help and aliases
01960 //Usage (of a derived class): Derived(specific-arguments) d; d.dispatch(command);
01961 //Derived classes should override virtual functions where noted.
01962 //The template parameter currently must be the dervived class itself,
01963 //i.e. class X : public map_command_handler<X>
01964 //To add a new command in a derived class:
01965 //  * add a new private void function() to the derived class
01966 //  * add it to the function map in init_map there, setting flags like
01967 //    "D" for debug only (checking the flag is also done in the derived class)
01968 //  * remember to add some help and/or usage information in init_map()
01969 template <class Worker>
01970 class map_command_handler
01971 {
01972     public:
01973         typedef void (Worker::*command_handler)();
01974         struct command
01975         {
01976             command_handler handler;
01977             std::string help; //long help text
01978             std::string usage; //only args info
01979             std::string flags;
01980             explicit command(command_handler h, const std::string help="",
01981                 const std::string& usage="", const std::string flags="")
01982             : handler(h), help(help), usage(usage), flags(flags)
01983             {
01984             }
01985             bool has_flag(const char f) const
01986             {
01987                 return flags.find(f) != flags.npos;
01988             }
01989             command& add_flag(const char f)
01990             {
01991                 flags += f;
01992                 return *this;
01993             }
01994         };
01995         typedef std::map<std::string, command> command_map;
01996         typedef std::map<std::string, std::string> command_alias_map;
01997 
01998         map_command_handler() : cap_("")
01999         {
02000         }
02001 
02002         virtual ~map_command_handler() {}
02003 
02004         bool empty() const
02005         {
02006             return command_map_.empty();
02007         }
02008         //actual work function
02009         void dispatch(std::string cmd)
02010         {
02011             if (empty()) {
02012                 init_map_default();
02013                 init_map();
02014             }
02015 
02016             // We recursively resolve alias (100 max to avoid infinite recursion)
02017             for (int i=0; i < 100; ++i) {
02018                 parse_cmd(cmd);
02019                 std::string actual_cmd = get_actual_cmd(get_cmd());
02020                 if (actual_cmd == get_cmd())
02021                     break;
02022                 std::string data = get_data(1);
02023                 // translate the command and add space + data if any
02024                 cmd = actual_cmd + (data.empty() ? "" : " ") + data;
02025             }
02026 
02027             if (get_cmd().empty()) {
02028                 return;
02029             }
02030 
02031             if (const command* c = get_command(get_cmd())) {
02032                 if (is_enabled(*c)) {
02033                     (static_cast<Worker*>(this)->*(c->handler))();
02034                 } else {
02035                     print(get_cmd(), _("This command is currently unavailable."));
02036                 }
02037             } else if (help_on_unknown_) {
02038                 utils::string_map symbols;
02039                 symbols["command"] = get_cmd();
02040                 symbols["help_command"] = cmd_prefix_ + "help";
02041                 print("help", VGETTEXT("Unknown command '$command', try $help_command "
02042                     "for a list of available commands.", symbols));
02043             }
02044         }
02045 
02046         std::vector<std::string> get_commands_list() const
02047         {
02048             std::vector<std::string> res;
02049             foreach(typename command_map::value_type i, command_map_) {
02050                 res.push_back(i.first);
02051             }
02052             return res;
02053         }
02054 
02055     protected:
02056         void init_map_default()
02057         {
02058             register_command("help", &map_command_handler<Worker>::help,
02059                 _("Available commands list and command-specific help. "
02060                 "Use \"help all\" to include currently unavailable commands."),
02061                 _("do not translate the 'all'^[all|<command>]"));
02062         }
02063         //derived classes initialize the map overriding this function
02064         virtual void init_map() = 0;
02065         //overridden in derived classes to actually print the messages somwehere
02066         virtual void print(const std::string& title, const std::string& message) = 0;
02067         //should be overridden in derived classes if the commands have flags
02068         //this should return a string describing what all the flags mean
02069         virtual std::string get_flags_description() const
02070         {
02071             return "";
02072         }
02073         //this should return a string describing the flags of the given command
02074         virtual std::string get_command_flags_description(const command& /*c*/) const
02075         {
02076             return "";
02077         }
02078         //this should be overridden if e.g. flags are used to control command
02079         //availability. Return false if the command should not be executed by dispatch()
02080         virtual bool is_enabled(const command& /*c*/) const
02081         {
02082             return true;
02083         }
02084         virtual void parse_cmd(const std::string& cmd_string)
02085         {
02086             cap_.parse(cmd_string);
02087         }
02088         //safe n-th argunment getter
02089         virtual std::string get_arg(unsigned argn) const
02090         {
02091             return cap_.get_arg(argn);
02092         }
02093         //"data" is n-th arg and everything after it
02094         virtual std::string get_data(unsigned argn = 1) const
02095         {
02096             return cap_.get_data(argn);
02097         }
02098         virtual std::string get_cmd() const
02099         {
02100             return cap_.get_cmd();
02101         }
02102         //command error reporting shorthands
02103         void command_failed(const std::string& message)
02104         {
02105             print(get_cmd(), _("Error:") + std::string(" ") + message);
02106         }
02107         void command_failed_need_arg(int argn)
02108         {
02109             utils::string_map symbols;
02110             symbols["arg_id"] = lexical_cast<std::string>(argn);
02111             command_failed(VGETTEXT("Missing argument $arg_id", symbols));
02112         }
02113         void print_usage()
02114         {
02115             help_command(get_cmd());
02116         }
02117         //take aliases into account
02118         std::string get_actual_cmd(const std::string& cmd) const
02119         {
02120             command_alias_map::const_iterator i = command_alias_map_.find(cmd);
02121             return i != command_alias_map_.end() ? i->second : cmd;
02122         }
02123         const command* get_command(const std::string& cmd) const
02124         {
02125             typename command_map::const_iterator i = command_map_.find(cmd);
02126             return i != command_map_.end() ? &i->second : 0;
02127         }
02128         command* get_command(const std::string& cmd)
02129         {
02130             typename command_map::iterator i = command_map_.find(cmd);
02131             return i != command_map_.end() ? &i->second : 0;
02132         }
02133         void help()
02134         {
02135             //print command-specific help if available, otherwise list commands
02136             if (help_command(get_arg(1))) {
02137                 return;
02138             }
02139             std::stringstream ss;
02140             bool show_unavail = show_unavailable_ || get_arg(1) == "all";
02141             BOOST_FOREACH(typename command_map::value_type i, command_map_) {
02142                 if (show_unavail || is_enabled(i.second)) {
02143                     ss << i.first;
02144                     //if (!i.second.usage.empty()) {
02145                     //  ss << " " << i.second.usage;
02146                     //}
02147                     //uncomment the above to display usage information in command list
02148                     //which might clutter it somewhat
02149                     if (!i.second.flags.empty()) {
02150                         ss << " (" << i.second.flags << ") ";
02151                     }
02152                     ss << "; ";
02153                 }
02154             }
02155             utils::string_map symbols;
02156             symbols["flags_description"] = get_flags_description();
02157             symbols["list_of_commands"] = ss.str();
02158             symbols["help_command"] = cmd_prefix_ + "help";
02159             print(_("help"), VGETTEXT("Available commands $flags_description:\n$list_of_commands", symbols));
02160             print(_("help"), VGETTEXT("Type $help_command <command> for more info.", symbols));
02161         }
02162         //returns true if the command exists.
02163         bool help_command(const std::string& acmd)
02164         {
02165             std::string cmd = get_actual_cmd(acmd);
02166             const command* c = get_command(cmd);
02167             if (c) {
02168                 std::stringstream ss;
02169                 ss << cmd_prefix_ << cmd;
02170                 if (c->help.empty() && c->usage.empty()) {
02171                     ss << _(" No help available.");
02172                 } else {
02173                     ss << " - " << c->help;
02174                 }
02175                 if (!c->usage.empty()) {
02176                     ss << " " << _("Usage:") << " " << cmd_prefix_ << cmd << " " << c->usage;
02177                 }
02178                 ss << get_command_flags_description(*c);
02179                 const std::vector<std::string> l = get_aliases(cmd);
02180                 if (!l.empty()) {
02181                     ss << " (" << _("aliases:") << " " << utils::join(l," ") << ")";
02182                 }
02183                 print(_("help"), ss.str());
02184             }
02185             return c != 0;
02186         }
02187         cmd_arg_parser cap_;
02188     protected:
02189         //show a "try help" message on unknown command?
02190         static void set_help_on_unknown(bool value)
02191         {
02192             help_on_unknown_ = value;
02193         }
02194         //this is display-only
02195         static void set_cmd_prefix(std::string value)
02196         {
02197             cmd_prefix_ = value;
02198         }
02199         virtual void register_command(const std::string& cmd,
02200             command_handler h, const std::string& help="",
02201             const std::string& usage="", const std::string& flags="")
02202         {
02203             command c = command(h, help, usage, flags);
02204             std::pair<typename command_map::iterator, bool> r;
02205             r = command_map_.insert(typename command_map::value_type(cmd, c));
02206             if (!r.second) { //overwrite if exists
02207                 r.first->second = c;
02208             }
02209         }
02210         virtual void assert_existence(const std::string& cmd) {
02211             assert(command_map_.count(cmd));
02212         }
02213         virtual void register_alias(const std::string& to_cmd,
02214             const std::string& cmd)
02215         {
02216             // disable the assert to allow alias to "command + args"
02217             // the fonction assert_existence seems unused now
02218             //assert_existence(to_cmd);
02219             command_alias_map_[cmd] = to_cmd;
02220         }
02221         //get all aliases of a command.
02222         static const std::vector<std::string> get_aliases(const std::string& cmd)
02223         {
02224             std::vector<std::string> aliases;
02225             typedef command_alias_map::value_type p;
02226             BOOST_FOREACH(p i, command_alias_map_) {
02227                 if (i.second == cmd) {
02228                     aliases.push_back(i.first);
02229                 }
02230             }
02231             return aliases;
02232         }
02233     private:
02234         static command_map command_map_;
02235         static command_alias_map command_alias_map_;
02236         static bool help_on_unknown_;
02237         static bool show_unavailable_;
02238         static std::string cmd_prefix_;
02239 };
02240 
02241 //static member definitions
02242 template <class Worker>
02243 typename map_command_handler<Worker>::command_map map_command_handler<Worker>::command_map_;
02244 
02245 template <class Worker>
02246 typename map_command_handler<Worker>::command_alias_map map_command_handler<Worker>::command_alias_map_;
02247 
02248 template <class Worker>
02249 bool map_command_handler<Worker>::help_on_unknown_ = true;
02250 
02251 template <class Worker>
02252 bool map_command_handler<Worker>::show_unavailable_ = false;
02253 
02254 template <class Worker>
02255 std::string map_command_handler<Worker>::cmd_prefix_;
02256 
02257 //command handler for chat /commands
02258 class chat_command_handler : public map_command_handler<chat_command_handler>
02259 {
02260     public:
02261         typedef map_command_handler<chat_command_handler> map;
02262         chat_command_handler(chat_handler& chathandler, bool allies_only)
02263         : map(), chat_handler_(chathandler), allies_only_(allies_only)
02264         {
02265         }
02266 
02267     protected:
02268         void do_emote();
02269         void do_network_send();
02270         void do_network_send_req_arg();
02271         void do_room_query();
02272         void do_room_query_noarg();
02273         void do_gen_room_query();
02274         void do_whisper();
02275         void do_chanmsg();
02276         void do_log();
02277         void do_ignore();
02278         void do_friend();
02279         void do_remove();
02280         void do_display();
02281         void do_version();
02282 
02283         /** Ask the server to register the currently used nick. */
02284         void do_register();
02285 
02286         /** Ask the server do drop the currently used (and registered) nick. */
02287         void do_drop();
02288 
02289         /** Update details for the currently used username. */
02290         void do_set();
02291 
02292         /** Request information about a user from the server. */
02293         void do_info();
02294 
02295         /**
02296          * Request a list of details that can be set for a username
02297          * as these might vary depending on the configuration of the server.
02298          */
02299         void do_details();
02300 
02301         std::string get_flags_description() const {
02302             return _("(A) — admin command");
02303         }
02304 
02305         std::string get_command_flags_description(
02306             const map_command_handler<chat_command_handler>::command& c) const
02307         {
02308             if (c.has_flag('A')) {
02309                 return std::string(" ") + _("(admin only)");
02310             } else {
02311                 return "";
02312             }
02313         }
02314 
02315         bool is_enabled(
02316             const map_command_handler<chat_command_handler>::command& c) const
02317         {
02318             return !(c.has_flag('A') && !preferences::is_authenticated());
02319         }
02320 
02321         void print(const std::string& title, const std::string& message)
02322         {
02323             chat_handler_.add_chat_message(time(NULL), title, 0, message);
02324         }
02325         void init_map()
02326         {
02327             set_cmd_prefix("/");
02328             register_command("query", &chat_command_handler::do_network_send,
02329                 _("Send a query to the server. Without arguments the server"
02330                 " should tell you the available commands."));
02331             register_alias("query", "q");
02332             register_command("ban", &chat_command_handler::do_network_send_req_arg,
02333                 _("Ban and kick a player or observer. If he is not in the"
02334                 " game but on the server he will only be banned."), _("<nickname>"));
02335             register_command("unban", &chat_command_handler::do_network_send_req_arg,
02336                 _("Unban a user. He does not have to be in the game but on"
02337                 " the server."), _("<nickname>"));
02338             register_command("kick", &chat_command_handler::do_network_send_req_arg,
02339                 _("Kick a player or observer."), _("<nickname>"));
02340             register_command("mute", &chat_command_handler::do_network_send,
02341                 _("Mute an observer. Without an argument displays the mute status."), _("<nickname>"));
02342             register_command("unmute", &chat_command_handler::do_network_send,
02343                 _("Unmute an observer. Without an argument unmutes everyone."), _("<nickname>"));
02344             register_command("muteall", &chat_command_handler::do_network_send,
02345                 _("Mute/Unmute all observers. (toggles)"), "");
02346             register_command("ping", &chat_command_handler::do_network_send,
02347                 "");
02348             register_command("green", &chat_command_handler::do_network_send_req_arg,
02349                 "", "", "A");
02350             register_command("red", &chat_command_handler::do_network_send_req_arg,
02351                 "", "", "A");
02352             register_command("yellow", &chat_command_handler::do_network_send_req_arg,
02353                 "", "", "A");
02354             register_command("report", &chat_command_handler::do_network_send_req_arg,
02355                 _("Report abuse, rule violations, etc. to the server moderators. "
02356                 "Make sure to mention relevant nicknames, etc."), "");
02357             register_alias("report", "adminmsg");  // deprecated
02358             register_command("emote", &chat_command_handler::do_emote,
02359                 _("Send an emotion or personal action in chat."), _("<message>"));
02360             register_alias("emote", "me");
02361             register_command("whisper", &chat_command_handler::do_whisper,
02362                 _("Sends a private message. "
02363                 "You cannot send private messages to players in a running game you observe or play in."),
02364                 _("<nickname> <message>"));
02365             register_alias("whisper", "msg");
02366             register_alias("whisper", "m");
02367             register_command("log", &chat_command_handler::do_log,
02368                 _("Change the log level of a log domain."), _("<level> <domain>"));
02369             register_command("ignore", &chat_command_handler::do_ignore,
02370                 _("Add a nickname to your ignores list."), _("<nickname>"));
02371             register_command("friend", &chat_command_handler::do_friend,
02372                 _("Add a nickname to your friends list."), _("<nickname>"));
02373             register_command("remove", &chat_command_handler::do_remove,
02374                 _("Remove a nickname from your ignores or friends list."), _("<nickname>"));
02375             register_command("list", &chat_command_handler::do_display,
02376                 _("Show your ignores and friends list."));
02377             register_alias("list", "display");
02378             register_command("version", &chat_command_handler::do_version,
02379                 _("Display version information."));
02380             register_command("register", &chat_command_handler::do_register,
02381                 _("Register your nickname"), _("<password> <email (optional)>"));
02382             register_command("drop", &chat_command_handler::do_drop,
02383                 _("Drop your nickname."));
02384             register_command("set", &chat_command_handler::do_set,
02385                 _("Update details for your nickname. For possible details see '/details'."),
02386                 _("<detail> <value>"));
02387             register_command("info", &chat_command_handler::do_info,
02388                 _("Request information about a nickname."), _("<nickname>"));
02389             register_command("details", &chat_command_handler::do_details,
02390                 _("Request a list of details you can set for your registered nickname."));
02391             register_command("join", &chat_command_handler::do_network_send_req_arg,
02392                 _("Join a room."), _("<room>"));
02393             register_alias("join", "j");
02394             register_command("part", &chat_command_handler::do_network_send_req_arg,
02395                 _("Part a room."), _("<room>"));
02396             register_command("names", &chat_command_handler::do_room_query,
02397                 _("List room members."), _("<room>"));
02398             register_command("rooms", &chat_command_handler::do_room_query_noarg,
02399                 _("List available rooms."));
02400             register_command("room", &chat_command_handler::do_chanmsg,
02401                 _("Room message."), _("<room> <msg>"));
02402             register_command("room_query", &chat_command_handler::do_gen_room_query,
02403                 _("Room query."), _("<room> <type> [value]"));
02404             register_alias("room_query", "rq");
02405         }
02406     private:
02407         chat_handler& chat_handler_;
02408         bool allies_only_;
02409 };
02410 
02411 //command handler for user :commands. Also understands all chat commands
02412 //via inheritance. This complicates some things a bit.
02413 class console_handler : public map_command_handler<console_handler>, private chat_command_handler
02414 {
02415     public:
02416         //convenience typedef
02417         typedef map_command_handler<console_handler> chmap;
02418         console_handler(menu_handler& menu_handler)
02419         : chmap(), chat_command_handler(menu_handler, true), menu_handler_(menu_handler), team_num_(resources::controller->current_side())
02420         {}
02421         using chmap::dispatch; //disambiguate
02422         using chmap::get_commands_list;
02423 
02424     protected:
02425         //chat_command_handler's init_map() and hanlers will end up calling these.
02426         //this makes sure the commands end up in our map
02427         virtual void register_command(const std::string& cmd,
02428             chat_command_handler::command_handler h, const std::string& help="",
02429             const std::string& usage="", const std::string& flags="")
02430         {
02431             chmap::register_command(cmd, h, help, usage, flags + "N"); //add chat commands as network_only
02432         }
02433         virtual void assert_existence(const std::string& cmd) {
02434             chmap::assert_existence(cmd);
02435         }
02436         virtual void register_alias(const std::string& to_cmd,
02437             const std::string& cmd)
02438         {
02439             chmap::register_alias(to_cmd, cmd);
02440         }
02441         virtual std::string get_arg(unsigned i) const
02442         {
02443             return chmap::get_arg(i);
02444         }
02445         virtual std::string get_cmd() const
02446         {
02447             return chmap::get_cmd();
02448         }
02449         virtual std::string get_data(unsigned n = 1) const
02450         {
02451             return chmap::get_data(n);
02452         }
02453 
02454         //these are needed to avoid ambiguities introduced by inheriting from console_command_handler
02455         using chmap::register_command;
02456         using chmap::register_alias;
02457         using chmap::help;
02458         using chmap::is_enabled;
02459         using chmap::command_failed;
02460         using chmap::command_failed_need_arg;
02461 
02462         void do_refresh();
02463         void do_droid();
02464         void do_theme();
02465         void do_control();
02466         void do_clear();
02467         void do_sunset();
02468         void do_foreground();
02469         void do_layers();
02470         void do_fps();
02471         void do_benchmark();
02472         void do_save();
02473         void do_save_quit();
02474         void do_quit();
02475         void do_ignore_replay_errors();
02476         void do_nosaves();
02477         void do_next_level();
02478         void do_choose_level();
02479         void do_turn();
02480         void do_turn_limit();
02481         void do_debug();
02482         void do_nodebug();
02483         void do_lua();
02484         void do_unsafe_lua();
02485         void do_custom();
02486         void do_set_alias();
02487         void do_set_var();
02488         void do_show_var();
02489         void do_inspect();
02490         void do_control_dialog();
02491         void do_manage();
02492         void do_unit();
02493         // void do_buff();
02494         // void do_unbuff();
02495         void do_discover();
02496         void do_undiscover();
02497         void do_create();
02498         void do_fog();
02499         void do_shroud();
02500         void do_gold();
02501         void do_event();
02502         void do_toggle_draw_coordinates();
02503         void do_toggle_draw_terrain_codes();
02504         void do_toggle_whiteboard();
02505         void do_whiteboard_options();
02506 
02507         std::string get_flags_description() const {
02508             return _("(D) — debug only, (N) — network only, (A) — admin only");
02509         }
02510         using chat_command_handler::get_command_flags_description; //silence a warning
02511         std::string get_command_flags_description(const chmap::command& c) const
02512         {
02513             std::string space(" ");
02514             return std::string(c.has_flag('D') ? space + _("(debug command)") : "")
02515                  + std::string(c.has_flag('N') ? space + _("(network only)") : "")
02516                  + std::string(c.has_flag('A') ? space + _("(admin only)") : "");
02517         }
02518         using map::is_enabled;
02519         bool is_enabled(const chmap::command& c) const
02520         {
02521             return !((c.has_flag('D') && !game_config::debug)
02522                   || (c.has_flag('N') && network::nconnections() == 0)
02523                   || (c.has_flag('A') && !preferences::is_authenticated()));
02524         }
02525         void print(const std::string& title, const std::string& message)
02526         {
02527             menu_handler_.add_chat_message(time(NULL), title, 0, message);
02528         }
02529         void init_map()
02530         {
02531             chat_command_handler::init_map();//grab chat_ /command handlers
02532             chmap::get_command("log")->flags = ""; //clear network-only flag from log
02533             chmap::get_command("version")->flags = ""; //clear network-only flag
02534             chmap::get_command("ignore")->flags = ""; //clear network-only flag
02535             chmap::get_command("friend")->flags = ""; //clear network-only flag
02536             chmap::get_command("list")->flags = ""; //clear network-only flag
02537             chmap::get_command("remove")->flags = ""; //clear network-only flag
02538             chmap::set_cmd_prefix(":");
02539             register_command("refresh", &console_handler::do_refresh,
02540                 _("Refresh gui."));
02541             register_command("droid", &console_handler::do_droid,
02542                 _("Switch a side to/from AI control."), _("do not translate the on/off^[<side> [on/off]]"));
02543             register_command("theme", &console_handler::do_theme);
02544             register_command("control", &console_handler::do_control,
02545                 _("Assign control of a side to a different player or observer."), _("<side> <nickname>"), "N");
02546             register_command("clear", &console_handler::do_clear,
02547                 _("Clear chat history."));
02548             register_command("sunset", &console_handler::do_sunset,
02549                 _("Visualize the screen refresh procedure."), "", "D");
02550             register_command("foreground", &console_handler::do_foreground,
02551                 _("Debug foreground terrain."), "", "D");
02552             register_command("layers", &console_handler::do_layers,
02553                 _("Debug layers from terrain under the mouse."), "", "D");
02554             register_command("fps", &console_handler::do_fps, _("Show fps."));
02555             register_command("benchmark", &console_handler::do_benchmark);
02556             register_command("save", &console_handler::do_save, _("Save game."));
02557             register_alias("save", "w");
02558             register_command("quit", &console_handler::do_quit, _("Quit game."));
02559             // Note the next value is used hardcoded in the init tests.
02560             register_alias("quit", "q!");
02561             register_command("save_quit", &console_handler::do_save_quit,
02562                 _("Save and quit."));
02563             register_alias("save_quit", "wq");
02564             register_command("ignore_replay_errors", &console_handler::do_ignore_replay_errors,
02565                 _("Ignore replay errors."));
02566             register_command("nosaves", &console_handler::do_nosaves,
02567                 _("Disable autosaves."));
02568             register_command("next_level", &console_handler::do_next_level,
02569                 _("Advance to the next scenario, or scenario identified by 'id'"), _("<id>"), "D");
02570             register_alias("next_level", "n");
02571             register_command("choose_level", &console_handler::do_choose_level,
02572                 _("Choose next scenario"), "", "D");
02573             register_alias("choose_level", "cl");
02574             register_command("turn", &console_handler::do_turn,
02575                 _("Change turn number (and time of day), or increase by one if no number is specified."), _("[turn]"), "D");
02576             register_command("turn_limit", &console_handler::do_turn_limit,
02577                 _("Change turn limit, or turn the turn limit off if no number is specified or it’s −1."), _("[limit]"), "D");
02578             register_command("debug", &console_handler::do_debug,
02579                 _("Turn debug mode on."));
02580             register_command("nodebug", &console_handler::do_nodebug,
02581                 _("Turn debug mode off."), "", "D");
02582             register_command("lua", &console_handler::do_lua,
02583                 _("Execute a Lua statement."), _("<command>[;<command>...]"), "D");
02584             register_command("unsafe_lua", &console_handler::do_unsafe_lua,
02585                 _("Grant higher privileges to Lua scripts."), "", "D");
02586             register_command("custom", &console_handler::do_custom,
02587                 _("Set the command used by the custom command hotkey"), _("<command>[;<command>...]"));
02588             register_command("give_control"
02589                     , &console_handler::do_control_dialog
02590                     , _("Invoke a dialog allowing changing control of MP sides.")
02591                     , ""
02592                     , "N");
02593             register_command("inspect", &console_handler::do_inspect,
02594                 _("Launch the gamestate inspector"), "", "D");
02595             register_command("manage", &console_handler::do_manage,
02596                 _("Manage persistence data"), "", "D");
02597             register_command("alias", &console_handler::do_set_alias,
02598                 _("Set or show alias to a command"), _("<name>[=<command>]"));
02599             register_command("set_var", &console_handler::do_set_var,
02600                 _("Set a scenario variable."), _("<var>=<value>"), "D");
02601             register_command("show_var", &console_handler::do_show_var,
02602                 _("Show a scenario variable."), _("<var>"), "D");
02603             register_command("unit", &console_handler::do_unit,
02604                 _("Modify a unit variable. (Only top level keys are supported.)"), "", "D");
02605 
02606             // register_command("buff", &console_handler::do_buff,
02607             //    _("Add a trait to a unit."), "", "D");
02608             // register_command("unbuff", &console_handler::do_unbuff,
02609             //    _("Remove a trait from a unit. (Does not work yet.)"), "", "D");
02610             register_command("discover", &console_handler::do_discover,
02611                 _("Discover all units in help."), "");
02612             register_command("undiscover", &console_handler::do_undiscover,
02613                   _("'Undiscover' all units in help."), "");
02614             register_command("create", &console_handler::do_create,
02615                 _("Create a unit."), "", "D");
02616             register_command("fog", &console_handler::do_fog,
02617                 _("Toggle fog for the current player."), "", "D");
02618             register_command("shroud", &console_handler::do_shroud,
02619                 _("Toggle shroud for the current player."), "", "D");
02620             register_command("gold", &console_handler::do_gold,
02621                 _("Give gold to the current player."), "", "D");
02622             register_command("throw", &console_handler::do_event,
02623                 _("Fire a game event."), "", "D");
02624             register_alias("throw", "fire");
02625             register_command("show_coordinates", &console_handler::do_toggle_draw_coordinates,
02626                 _("Toggle overlaying of x,y coordinates on hexes."));
02627             register_alias("show_coordinates", "sc");
02628             register_command("show_terrain_codes", &console_handler::do_toggle_draw_terrain_codes,
02629                 _("Toggle overlaying of terrain codes on hexes."));
02630             register_alias("show_terrain_codes", "tc");
02631             register_command("whiteboard", &console_handler::do_toggle_whiteboard,
02632                 _("Toggle planning mode."));
02633             register_alias("whiteboard", "wb");
02634             register_command("whiteboard_options", &console_handler::do_whiteboard_options,
02635                 _("Access whiteboard options dialog."));
02636             register_alias("whiteboard_options", "wbo");
02637 
02638             if (const config &alias_list = preferences::get_alias())
02639             {
02640                 foreach (const config::attribute &a, alias_list.attribute_range()) {
02641                     register_alias(a.second, a.first);
02642                 }
02643             }
02644         }
02645     private:
02646         menu_handler& menu_handler_;
02647         const unsigned int team_num_;
02648 };
02649 
02650 chat_handler::chat_handler()
02651 {
02652 }
02653 
02654 chat_handler::~chat_handler()
02655 {
02656 }
02657 
02658 /**
02659  * Change the log level of a log domain.
02660  *
02661  * @param data string of the form: "@<level@> @<domain@>"
02662  */
02663 void chat_handler::change_logging(const std::string& data) {
02664     const std::string::const_iterator j =
02665             std::find(data.begin(), data.end(), ' ');
02666     if (j == data.end()) return;
02667     const std::string level(data.begin(),j);
02668     const std::string domain(j+1,data.end());
02669     int severity;
02670     if (level == "error") severity = 0;
02671     else if (level == "warning") severity = 1;
02672     else if (level == "info") severity = 2;
02673     else if (level == "debug") severity = 3;
02674     else {
02675         utils::string_map symbols;
02676         symbols["level"] = level;
02677         const std::string& msg =
02678                 vgettext("Unknown debug level: '$level'.", symbols);
02679         ERR_NG << msg << "\n";
02680         add_chat_message(time(NULL), _("error"), 0, msg);
02681         return;
02682     }
02683     if (!lg::set_log_domain_severity(domain, severity)) {
02684         utils::string_map symbols;
02685         symbols["domain"] = domain;
02686         const std::string& msg =
02687                 vgettext("Unknown debug domain: '$domain'.", symbols);
02688         ERR_NG << msg << "\n";
02689         add_chat_message(time(NULL), _("error"), 0, msg);
02690         return;
02691     } else {
02692         utils::string_map symbols;
02693         symbols["level"] = level;
02694         symbols["domain"] = domain;
02695         const std::string& msg =
02696                 vgettext("Switched domain: '$domain' to level: '$level'.", symbols);
02697         LOG_NG << msg << "\n";
02698         add_chat_message(time(NULL), "log", 0, msg);
02699     }
02700 }
02701 
02702 void chat_handler::send_command(const std::string& cmd, const std::string& args /* = "" */) {
02703     config data;
02704     if (cmd == "muteall") {
02705         data.add_child(cmd);
02706     } else if (cmd == "query") {
02707         data.add_child(cmd)["type"] = args;
02708     } else if (cmd == "ban" || cmd == "unban" || cmd == "kick"
02709     || cmd == "mute" || cmd == "unmute") {
02710         data.add_child(cmd)["username"] = args;
02711     } else if (cmd == "ping") {
02712         data[cmd] = lexical_cast<std::string>(time(NULL));
02713     } else if (cmd == "green") {
02714         data.add_child("query")["type"] = "lobbymsg @" + args;
02715     } else if (cmd == "red") {
02716         data.add_child("query")["type"] = "lobbymsg #" + args;
02717     } else if (cmd == "yellow") {
02718         data.add_child("query")["type"] = "lobbymsg <255,255,0>" + args;
02719     } else if (cmd == "report") {
02720         data.add_child("query")["type"] = "report " + args;
02721     } else if (cmd == "join") {
02722         data.add_child("room_join")["room"] = args;
02723     } else if (cmd == "part") {
02724         data.add_child("room_part")["room"] = args;
02725     }
02726     network::send_data(data, 0);
02727 }
02728 
02729 void chat_handler::do_speak(const std::string& message, bool allies_only)
02730 {
02731     if(message == "" || message == "/") {
02732         return;
02733     }
02734     bool is_command = (message[0] == '/');
02735     bool quoted_command = (is_command && message[1] == ' ');
02736 
02737     if(!is_command) {
02738         send_chat_message(message, allies_only);
02739         return;
02740     } else if (quoted_command) {
02741         send_chat_message(std::string(message.begin() + 2, message.end()), allies_only);
02742         return;
02743     }
02744     std::string cmd(message.begin() + 1, message.end());
02745     chat_command_handler cch(*this, allies_only);
02746     cch.dispatch(cmd);
02747 }
02748 
02749 void chat_handler::user_relation_changed(const std::string& /*name*/)
02750 {
02751 }
02752 
02753 void chat_handler::send_whisper(const std::string& receiver, const std::string& message)
02754 {
02755     config cwhisper, data;
02756     cwhisper["receiver"] = receiver;
02757     cwhisper["message"] = message;
02758     cwhisper["sender"] = preferences::login();
02759     data.add_child("whisper", cwhisper);
02760     network::send_data(data, 0);
02761 }
02762 
02763 void chat_handler::add_whisper_sent(const std::string& receiver, const std::string& message)
02764 {
02765     utils::string_map symbols;
02766     symbols["receiver"] = receiver;
02767     add_chat_message(time(NULL), VGETTEXT("whisper to $receiver", symbols), 0, message);
02768 }
02769 
02770 void chat_handler::add_whisper_received(const std::string& sender, const std::string& message)
02771 {
02772     utils::string_map symbols;
02773     symbols["sender"] = sender;
02774     add_chat_message(time(NULL), VGETTEXT("whisper: $sender", symbols), 0, message);
02775 }
02776 
02777 void chat_handler::send_chat_room_message(const std::string& room,
02778     const std::string& message)
02779 {
02780     config cmsg, data;
02781     cmsg["room"] = room;
02782     cmsg["message"] = message;
02783     cmsg["sender"] = preferences::login();
02784     data.add_child("message", cmsg);
02785     network::send_data(data, 0);
02786 }
02787 
02788 void chat_handler::add_chat_room_message_sent(const std::string &room, const std::string &message)
02789 {
02790     add_chat_room_message_received(room, preferences::login(), message);
02791 }
02792 
02793 void chat_handler::add_chat_room_message_received(const std::string &room,
02794     const std::string &speaker, const std::string &message)
02795 {
02796     add_chat_message(time(NULL), room + ": " + speaker, 0, message, events::chat_handler::MESSAGE_PRIVATE);
02797 }
02798 
02799 
02800 void chat_command_handler::do_emote()
02801 {
02802     chat_handler_.send_chat_message("/me " + get_data(), allies_only_);
02803 }
02804 
02805 void chat_command_handler::do_network_send()
02806 {
02807     chat_handler_.send_command(get_cmd(), get_data());
02808 }
02809 
02810 void chat_command_handler::do_network_send_req_arg()
02811 {
02812     if (get_data(1).empty()) return command_failed_need_arg(1);
02813     do_network_send();
02814 }
02815 
02816 void chat_command_handler::do_room_query_noarg()
02817 {
02818     config data;
02819     config& q = data.add_child("room_query");
02820     q.add_child(get_cmd());
02821     network::send_data(data, 0);
02822 }
02823 
02824 void chat_command_handler::do_room_query()
02825 {
02826     if (get_data(1).empty()) return command_failed_need_arg(1);
02827     config data;
02828     config& q = data.add_child("room_query");
02829     q["room"] = get_arg(1);
02830     q.add_child(get_cmd());
02831     network::send_data(data, 0);
02832 }
02833 
02834 void chat_command_handler::do_gen_room_query()
02835 {
02836     if (get_data(1).empty()) return command_failed_need_arg(1);
02837     config data;
02838     config& q = data.add_child("room_query");
02839     q["room"] = get_arg(1);
02840     config& c = q.add_child(get_arg(2));
02841     c["value"] = get_data(3);
02842     network::send_data(data, 0);
02843 }
02844 
02845 void chat_command_handler::do_whisper()
02846 {
02847     if (get_data(1).empty()) return command_failed_need_arg(1);
02848     if (get_data(2).empty()) return command_failed_need_arg(2);
02849     chat_handler_.send_whisper(get_arg(1), get_data(2));
02850     chat_handler_.add_whisper_sent(get_arg(1), get_data(2));
02851 }
02852 
02853 void chat_command_handler::do_chanmsg()
02854 {
02855     if (get_data(1).empty()) return command_failed_need_arg(1);
02856     if (get_data(2).empty()) return command_failed_need_arg(2);
02857     chat_handler_.send_chat_room_message(get_arg(1), get_data(2));
02858     chat_handler_.add_chat_room_message_sent(get_arg(1), get_data(2));
02859 }
02860 
02861 void chat_command_handler::do_log()
02862 {
02863     chat_handler_.change_logging(get_data());
02864 }
02865 
02866 void chat_command_handler::do_ignore()
02867 {
02868     if (get_arg(1).empty()) {
02869         const std::set<std::string>& tmp = preferences::get_ignores();
02870         print(_("ignores list"), tmp.empty() ? _("(empty)") : utils::join(tmp));
02871     } else {
02872         for(int i = 1; !get_arg(i).empty(); i++){
02873             utils::string_map symbols;
02874             symbols["nick"] = get_arg(i);
02875             if (preferences::add_ignore(get_arg(i))) {
02876                 print(_("ignores list"),  VGETTEXT("Added to ignore list: $nick", symbols));
02877                 chat_handler_.user_relation_changed(get_arg(i));
02878             } else {
02879                 command_failed(VGETTEXT("Invalid username: $nick", symbols));
02880             }
02881         }
02882     }
02883 }
02884 
02885 void chat_command_handler::do_friend()
02886 {
02887     if (get_arg(1).empty()) {
02888         const std::set<std::string>& tmp = preferences::get_friends();
02889         print(_("friends list"), tmp.empty() ? _("(empty)") : utils::join(tmp));
02890     } else {
02891         for(int i = 1;!get_arg(i).empty();i++){
02892             utils::string_map symbols;
02893             symbols["nick"] = get_arg(i);
02894             if (preferences::add_friend(get_arg(i))) {
02895                 chat_handler_.user_relation_changed(get_arg(i));
02896                 print(_("friends list"),  VGETTEXT("Added to friends list: $nick", symbols));
02897             } else {
02898                 command_failed(VGETTEXT("Invalid username: $nick", symbols));
02899             }
02900         }
02901     }
02902 }
02903 
02904 void chat_command_handler::do_remove()
02905 {
02906     for(int i = 1;!get_arg(i).empty();i++){
02907         preferences::remove_friend(get_arg(i));
02908         preferences::remove_ignore(get_arg(i));
02909         chat_handler_.user_relation_changed(get_arg(i));
02910         utils::string_map symbols;
02911         symbols["nick"] = get_arg(i);
02912         print(_("friends and ignores list"), VGETTEXT("Removed from list: $nick", symbols));
02913     }
02914 }
02915 
02916 void chat_command_handler::do_display()
02917 {
02918     const std::set<std::string> & friends = preferences::get_friends();
02919     const std::set<std::string> & ignores = preferences::get_ignores();
02920 
02921     if (!friends.empty()) {
02922         print(_("friends list"), utils::join(friends));
02923     }
02924 
02925     if (!ignores.empty()) {
02926         print(_("ignores list"), utils::join(ignores));
02927     }
02928 
02929     if (friends.empty() && ignores.empty()) {
02930         print(_("friends and ignores list"), _("There are no players on your friends or ignore list."));
02931     }
02932 }
02933 
02934 void chat_command_handler::do_version() {
02935     print(_("version"), game_config::revision);
02936 }
02937 
02938 void chat_command_handler::do_register() {
02939     config data;
02940     config& nickserv = data.add_child("nickserv");
02941 
02942     if (get_data(1).empty()) return command_failed_need_arg(1);
02943 
02944     config &reg = nickserv.add_child("register");
02945     reg["password"] = get_arg(1);
02946     if(!get_data(2).empty()) {
02947         reg["mail"] = get_arg(2);
02948     }
02949     std::string msg;
02950     if (get_data(2).empty()) {
02951         msg = _("registering with password *** and no email address");
02952     } else {
02953         utils::string_map symbols;
02954         symbols["email"] = get_data(2);
02955         msg = VGETTEXT("registering with password *** and "
02956             "email address $email", symbols);
02957     }
02958     print(_("nick registration"), msg);
02959 
02960     network::send_data(data, 0);
02961 }
02962 
02963 void chat_command_handler::do_drop() {
02964     config data;
02965     config& nickserv = data.add_child("nickserv");
02966 
02967     nickserv.add_child("drop");
02968 
02969     print(_("nick registration"), _("dropping your username"));
02970 
02971     network::send_data(data, 0);
02972 }
02973 
02974 void chat_command_handler::do_set() {
02975     config data;
02976     config& nickserv = data.add_child("nickserv");
02977 
02978     if (get_data(1).empty()) return command_failed_need_arg(1);
02979     if (get_data(2).empty()) return command_failed_need_arg(2);
02980 
02981     config &set = nickserv.add_child("set");
02982     set["detail"] = get_arg(1);
02983     set["value"] = get_data(2);
02984     utils::string_map symbols;
02985     symbols["var"] = get_arg(1);
02986     symbols["value"] = get_arg(2);
02987     print(_("nick registration"), VGETTEXT("setting $var to $value", symbols));
02988 
02989     network::send_data(data, 0);
02990 }
02991 
02992 void chat_command_handler::do_info() {
02993     if (get_data(1).empty()) return command_failed_need_arg(1);
02994 
02995     config data;
02996     config& nickserv = data.add_child("nickserv");
02997 
02998     nickserv.add_child("info")["name"] = get_data(1);
02999     utils::string_map symbols;
03000     symbols["nick"] = get_arg(1);
03001     print(_("nick registration"), VGETTEXT("requesting information for user $nick", symbols));
03002 
03003     network::send_data(data, 0);
03004 }
03005 
03006 void chat_command_handler::do_details() {
03007 
03008     config data;
03009     config& nickserv = data.add_child("nickserv");
03010     nickserv.add_child("details");
03011 
03012     network::send_data(data, 0);
03013 }
03014 
03015 void menu_handler::send_chat_message(const std::string& message, bool allies_only)
03016 {
03017     config cfg;
03018     cfg["id"] = preferences::login();
03019     cfg["message"] = message;
03020 
03021     const int side = is_observer() ? 0 : gui_->viewing_side();
03022     if(!is_observer()) {
03023         cfg["side"] = side;
03024     }
03025 
03026     bool private_message = has_friends() && allies_only;
03027 
03028     if(private_message) {
03029         if (is_observer()) {
03030             cfg["team_name"] = game_config::observer_team_name;
03031         } else {
03032             cfg["team_name"] = teams_[gui_->viewing_team()].team_name();
03033         }
03034     }
03035 
03036     recorder.speak(cfg);
03037     add_chat_message(time(NULL), cfg["id"], side, message,
03038             private_message ? events::chat_handler::MESSAGE_PRIVATE : events::chat_handler::MESSAGE_PUBLIC);
03039 
03040 }
03041 
03042 
03043 void menu_handler::do_search(const std::string& new_search)
03044 {
03045     if(new_search.empty() == false && new_search != last_search_)
03046         last_search_ = new_search;
03047 
03048     if(last_search_.empty()) return;
03049 
03050     bool found = false;
03051     map_location loc = last_search_hit_;
03052     //If this is a location search, just center on that location.
03053     std::vector<std::string> args = utils::split(last_search_, ',');
03054     if(args.size() == 2) {
03055         int x, y;
03056         x = lexical_cast_default<int>(args[0], 0)-1;
03057         y = lexical_cast_default<int>(args[1], 0)-1;
03058         if(x >= 0 && x < map_.w() && y >= 0 && y < map_.h()) {
03059             loc = map_location(x,y);
03060             found = true;
03061         }
03062     }
03063     //Start scanning the game map
03064     if(loc.valid() == false)
03065         loc = map_location(map_.w()-1,map_.h()-1);
03066     map_location start = loc;
03067     while (!found) {
03068         //Move to the next location
03069         loc.x = (loc.x + 1) % map_.w();
03070         if(loc.x == 0)
03071             loc.y = (loc.y + 1) % map_.h();
03072 
03073         //Search label
03074         if (!gui_->shrouded(loc)) {
03075             const terrain_label* label = gui_->labels().get_label(loc);
03076             if(label) {
03077                 std::string label_text = label->text().str();
03078                 if(std::search(label_text.begin(), label_text.end(),
03079                         last_search_.begin(), last_search_.end(),
03080                         chars_equal_insensitive) != label_text.end()) {
03081                     found = true;
03082                 }
03083             }
03084         }
03085         //Search unit name
03086         if (!gui_->fogged(loc)) {
03087             unit_map::const_iterator ui = units_.find(loc);
03088             if(ui != units_.end()) {
03089                 const std::string name = ui->name();
03090                 if(std::search(name.begin(), name.end(),
03091                         last_search_.begin(), last_search_.end(),
03092                         chars_equal_insensitive) != name.end()) {
03093                     if (!teams_[gui_->viewing_team()].is_enemy(ui->side()) ||
03094                         !ui->invisible(ui->get_location())) {
03095                         found = true;
03096                     }
03097                 }
03098             }
03099         }
03100 
03101         if(loc == start)
03102             break;
03103     }
03104 
03105     if(found) {
03106         last_search_hit_ = loc;
03107         gui_->scroll_to_tile(loc,game_display::ONSCREEN,false);
03108         gui_->highlight_hex(loc);
03109     } else {
03110         last_search_hit_ = map_location();
03111         //Not found, inform the player
03112         utils::string_map symbols;
03113         symbols["search"] = last_search_;
03114         const std::string msg = vgettext("Couldn't find label or unit "
03115                 "containing the string '$search'.", symbols);
03116         gui::dialog(*gui_,"",msg).show();
03117     }
03118 }
03119 
03120 void menu_handler::do_command(const std::string& str)
03121 {
03122     console_handler ch(*this);
03123     ch.dispatch(str);
03124 }
03125 
03126 std::vector<std::string> menu_handler::get_commands_list()
03127 {
03128     console_handler ch(*this);
03129     return ch.get_commands_list();
03130 }
03131 
03132 void console_handler::do_refresh() {
03133     image::flush_cache();
03134     menu_handler_.gui_->redraw_everything();
03135 }
03136 
03137 void console_handler::do_droid() {
03138     // :droid [<side> [on/off]]
03139     const std::string side_s = get_arg(1);
03140     const std::string action = get_arg(2);
03141     // default to the current side if empty
03142     const unsigned int side = side_s.empty() ?
03143         team_num_ : lexical_cast_default<unsigned int>(side_s);
03144 
03145     if (side < 1 || side > menu_handler_.teams_.size()) {
03146         utils::string_map symbols;
03147         symbols["side"] = side_s;
03148         command_failed(vgettext("Can't droid invalid side: '$side'.", symbols));
03149         return;
03150     } else if (menu_handler_.teams_[side - 1].is_network()) {
03151         utils::string_map symbols;
03152         symbols["side"] = lexical_cast<std::string>(side);
03153         command_failed(vgettext("Can't droid networked side: '$side'.", symbols));
03154         return;
03155     } else if (menu_handler_.teams_[side - 1].is_human() && action != " off") {
03156         //this is our side, so give it to AI
03157         menu_handler_.teams_[side - 1].make_human_ai();
03158         menu_handler_.change_controller(lexical_cast<std::string>(side),"human_ai");
03159         if(team_num_ == side) {
03160             //if it is our turn at the moment, we have to indicate to the
03161             //play_controller, that we are no longer in control
03162             throw end_turn_exception(side);
03163         }
03164     } else if (menu_handler_.teams_[side - 1].is_ai() && action != " on") {
03165         menu_handler_.teams_[side - 1].make_human();
03166         menu_handler_.change_controller(lexical_cast<std::string>(side),"human");
03167     }
03168     menu_handler_.textbox_info_.close(*menu_handler_.gui_);
03169 }
03170 
03171 void console_handler::do_theme() {
03172     preferences::show_theme_dialog(*menu_handler_.gui_);
03173 }
03174 void console_handler::do_control() {
03175     // :control <side> <nick>
03176     if (network::nconnections() == 0) return;
03177     const std::string side = get_arg(1);
03178     const std::string player = get_arg(2);
03179     if(player.empty())
03180     {
03181         command_failed_need_arg(2);
03182         return;
03183     }
03184 
03185     unsigned int side_num;
03186     try {
03187         side_num = lexical_cast<unsigned int>(side);
03188     } catch(bad_lexical_cast&) {
03189         utils::string_map symbols;
03190         symbols["side"] = side;
03191         command_failed(vgettext("Can't change control of invalid side: '$side'.", symbols));
03192         return;
03193     }
03194     if (side_num < 1 || side_num > menu_handler_.teams_.size()) {
03195         utils::string_map symbols;
03196         symbols["side"] = side;
03197         command_failed(vgettext("Can't change control of out-of-bounds side: '$side'.", symbols));
03198         return;
03199     }
03200     menu_handler_.request_control_change(side_num,player);
03201     menu_handler_.textbox_info_.close(*(menu_handler_.gui_));
03202 }
03203 void console_handler::do_clear() {
03204     menu_handler_.gui_->clear_chat_messages();
03205 }
03206 void console_handler::do_sunset() {
03207     int delay = lexical_cast_default<int>(get_data());
03208     menu_handler_.gui_->sunset(delay);
03209     gui2::twindow::set_sunset(delay);
03210 }
03211 void console_handler::do_foreground() {
03212     menu_handler_.gui_->toggle_debug_foreground();
03213 }
03214 
03215 void console_handler::do_layers() {
03216     const mouse_handler& mousehandler = resources::controller->get_mouse_handler_base();
03217     const map_location &loc = mousehandler.get_last_hex();
03218 
03219     std::vector<std::string> layers;
03220     //NOTE: columns reflect WML keys, don't translate them
03221     std::string heading = std::string(1,HEADING_PREFIX) +
03222         "^#"      + COLUMN_SEPARATOR + // 0
03223         "Image"   + COLUMN_SEPARATOR + // 1
03224         "Name"    + COLUMN_SEPARATOR + // 2
03225         "Loc"     + COLUMN_SEPARATOR + // 3
03226         "Layer"   + COLUMN_SEPARATOR + // 4
03227         "Base.x"  + COLUMN_SEPARATOR + // 5
03228         "Base.y"  + COLUMN_SEPARATOR + // 6
03229         "Center"                       // 7
03230 
03231     ;
03232     layers.push_back(heading);
03233 
03234     display& disp = *(menu_handler_.gui_);
03235     terrain_builder& builder = disp.get_builder();
03236     terrain_builder::tile* tile = builder.get_tile(loc);
03237 
03238     const std::string& tod_id = disp.get_time_of_day(loc).id;
03239     terrain_builder::tile::logs tile_logs;
03240     tile->rebuild_cache(tod_id, &tile_logs);
03241 
03242     int order = 1;
03243     foreach(const terrain_builder::tile::log_details det, tile_logs) {
03244         const terrain_builder::tile::rule_image_rand& ri = *det.first;
03245         const terrain_builder::rule_image_variant& variant = *det.second;
03246 
03247         /** @todo also use random image variations (not just take 1st) */
03248         const image::locator& img = variant.images.front().get_first_frame();
03249         const std::string& name = img.get_filename();
03250         /** @todo deal with (rarely used) ~modifications */
03251         //const std::string& modif = img.get_modifications();
03252         const map_location& loc_cut = img.get_loc();
03253 
03254         std::ostringstream str;
03255 
03256         int tz = image::tile_size;
03257         SDL_Rect r = create_rect(0,0,tz,tz);
03258 
03259         surface surf = image::get_image(img.get_filename());
03260 
03261         // calculate which part of the image the terrain engine uses
03262         if(loc_cut.valid()) {
03263             // copied from image.cpp : load_image_sub_file()
03264             r = create_rect(
03265                     ((tz*3) / 4) * loc_cut.x
03266                     , tz * loc_cut.y + (tz / 2) * (loc_cut.x % 2)
03267                     , tz, tz);
03268 
03269             if(img.get_center_x() >= 0 && img.get_center_y()>= 0){
03270                 r.x += surf->w/2 - img.get_center_x();
03271                 r.y += surf->h/2 - img.get_center_y();
03272             }
03273         }
03274 
03275         str << (ri->is_background() ? "B ": "F ") << order
03276             << COLUMN_SEPARATOR
03277             << IMAGE_PREFIX << "terrain/foreground.png";
03278 
03279         // cut and mask the image
03280         // ~CROP and ~BLIT have limitations, we do some math to avoid them
03281         SDL_Rect r2 = intersect_rects(r, create_rect(0,0,surf->w,surf->h));
03282         if(r2.w > 0 && r2.h > 0) {
03283             str << "~BLIT("
03284                     << name << "~CROP("
03285                         << r2.x << "," << r2.y << ","
03286                         << r2.w << "," << r2.h
03287                     << ")"
03288                     << "," << r2.x-r.x << "," << r2.y-r.y
03289                 << ")"
03290                 << "~MASK(" << "terrain/alphamask.png" << ")";
03291         }
03292 
03293         str << COLUMN_SEPARATOR
03294             << IMAGE_PREFIX  << name << "~SCALE(72,72)"
03295             << IMG_TEXT_SEPARATOR << name
03296             << COLUMN_SEPARATOR << img.get_loc()
03297             << COLUMN_SEPARATOR << ri->layer
03298             << COLUMN_SEPARATOR << ri->basex
03299             << COLUMN_SEPARATOR << ri->basey
03300             << COLUMN_SEPARATOR
03301             << ri->center_x << ", " << ri->center_y;
03302         layers.push_back(str.str());
03303         ++order;
03304     }
03305 
03306     std::vector<std::string> flags(tile->flags.begin(),tile->flags.end());
03307     std::ostringstream info;
03308     // NOTE using ", " also allows better word wrapping
03309     info << "Flags :" << utils::join(flags, ", ");
03310     {
03311         gui::dialog menu(*menu_handler_.gui_, _("Layers"), info.str(), gui::OK_CANCEL);
03312         menu.set_menu(layers);
03313         menu.show();
03314     }
03315 }
03316 void console_handler::do_fps() {
03317     preferences::set_show_fps(!preferences::show_fps());
03318 }
03319 void console_handler::do_benchmark() {
03320     menu_handler_.gui_->toggle_benchmark();
03321 }
03322 void console_handler::do_save() {
03323     savegame::savegame save(menu_handler_.gamestate_, preferences::compress_saves());
03324     save.save_game_automatic(menu_handler_.gui_->video(), true, get_data());
03325 }
03326 void console_handler::do_save_quit() {
03327     savegame::savegame save(menu_handler_.gamestate_, preferences::compress_saves());
03328     save.save_game_automatic(menu_handler_.gui_->video(), true, get_data());
03329     throw end_level_exception(QUIT);
03330 }
03331 void console_handler::do_quit() {
03332     throw end_level_exception(QUIT);
03333 }
03334 void console_handler::do_ignore_replay_errors() {
03335     game_config::ignore_replay_errors = (get_data() != "off") ? true : false;
03336 }
03337 void console_handler::do_nosaves() {
03338     game_config::disable_autosave = (get_data() != "off") ? true : false;
03339 }
03340 
03341 void console_handler::do_next_level()
03342 {
03343     if (!get_data().empty())
03344         menu_handler_.gamestate_.classification().next_scenario = get_data();
03345     end_level_data &e = resources::controller->get_end_level_data();
03346     e.carryover_percentage = 100;
03347     e.carryover_add = false;
03348     e.gold_bonus = false;
03349     e.carryover_report = false;
03350     e.prescenario_save = true;
03351     e.linger_mode = false;
03352     throw end_level_exception(VICTORY);
03353 }
03354 
03355 void console_handler::do_choose_level() {
03356     std::vector<std::string> options;
03357     int next = 0, nb = 0;
03358     foreach (const config &sc, menu_handler_.game_config_.child_range("scenario"))
03359     {
03360         const std::string &id = sc["id"];
03361         options.push_back(id);
03362         if (id == menu_handler_.gamestate_.classification().next_scenario)
03363             next = nb;
03364         ++nb;
03365     }
03366     // find scenarios of multiplayer campaigns
03367     // (assumes that scenarios are ordered properly in the game_config)
03368     std::string& scenario = menu_handler_.gamestate_.mp_settings().mp_scenario;
03369     foreach (const config &mp, menu_handler_.game_config_.child_range("multiplayer"))
03370     {
03371         if (mp["id"] == scenario)
03372         {
03373             const std::string &id = mp["id"];
03374             options.push_back(id);
03375             if (id == menu_handler_.gamestate_.classification().next_scenario)
03376                 next = nb;
03377             ++nb;
03378             scenario = mp["next_scenario"].str();
03379         }
03380     }
03381     std::sort(options.begin(), options.end());
03382     int choice = 0;
03383     {
03384         gui2::tsimple_item_selector dlg(_("Choose Scenario (Debug!)"), "", options);
03385         dlg.set_selected_index(next);
03386         dlg.show(menu_handler_.gui_->video());
03387         choice = dlg.selected_index();
03388     }
03389 
03390     if(choice == -1)
03391         return;
03392 
03393     if (size_t(choice) < options.size()) {
03394         menu_handler_.gamestate_.classification().next_scenario = options[choice];
03395         end_level_data &e = resources::controller->get_end_level_data();
03396         e.carryover_percentage = 100;
03397         e.carryover_add = false;
03398         e.gold_bonus = false;
03399         e.carryover_report = false;
03400         e.prescenario_save = true;
03401         e.linger_mode = false;
03402         throw end_level_exception(VICTORY);
03403     }
03404 }
03405 
03406 void console_handler::do_turn()
03407 {
03408     int turn = resources::tod_manager->turn() + 1;
03409     const std::string& data = get_data();
03410     if (!data.empty()) {
03411         turn = lexical_cast_default<int>(data, 1);
03412     }
03413     resources::tod_manager->set_turn(turn);
03414 
03415     menu_handler_.gui_->new_turn();
03416     menu_handler_.gui_->redraw_everything();
03417 }
03418 
03419 void console_handler::do_turn_limit()
03420 {
03421     tod_manager& tod_man = *resources::tod_manager;
03422     int limit =
03423         get_data().empty() ? -1 : lexical_cast_default<int>(get_data(), 1);
03424     tod_man.set_number_of_turns(limit);
03425     menu_handler_.gui_->redraw_everything();
03426 }
03427 
03428 void console_handler::do_debug() {
03429     if (network::nconnections() == 0 || game_config::mp_debug) {
03430         print(get_cmd(), _("Debug mode activated!"));
03431         game_config::debug = true;
03432     } else {
03433         command_failed(_("Debug mode not available in network games"));
03434     }
03435 }
03436 void console_handler::do_nodebug() {
03437     if (game_config::debug) {
03438         print(get_cmd(), _("Debug mode deactivated!"));
03439         game_config::debug = false;
03440     }
03441 }
03442 void console_handler::do_lua() {
03443     resources::lua_kernel->run(get_data().c_str());
03444     game_events::commit();
03445 }
03446 
03447 void console_handler::do_unsafe_lua()
03448 {
03449     if (gui2::show_message(resources::screen->video(), _("Unsafe Lua scripts."),
03450         _("You are about to open a security breach in Wesnoth. Are you sure you want to continue? If you have downloaded add-ons, do not click 'ok'! They would instantly take over your computer. You have been warned."),
03451         gui2::tmessage::ok_cancel_buttons) == gui2::twindow::OK)
03452     {
03453         print(get_cmd(), _("Unsafe mode enabled!"));
03454         resources::lua_kernel->load_package();
03455     }
03456 }
03457 
03458 void console_handler::do_custom() {
03459     preferences::set_custom_command(get_data());
03460 }
03461 void console_handler::do_set_alias() {
03462     const std::string data = get_data();
03463     const std::string::const_iterator j = std::find(data.begin(),data.end(),'=');
03464     const std::string alias(data.begin(),j);
03465     if(j != data.end()) {
03466         const std::string command(j+1,data.end());
03467         if (!command.empty()) {
03468             register_alias(command, alias);
03469         } else {
03470             // "alias something=" deactivate this alias. We just set it
03471             // equal to itself here. Later preferences will filter empty alias.
03472             register_alias(alias, alias);
03473         }
03474         preferences::add_alias(alias, command);
03475         // directly save it for the moment, but will slow commands sequence
03476         preferences::write_preferences();
03477     } else {
03478         // "alias something" display its value
03479         // if no alias, will be "'something' = 'something'"
03480         const std::string command = chmap::get_actual_cmd(alias);
03481         print(get_cmd(), "'"+alias+"'" + " = " + "'"+command+"'");
03482     }
03483 }
03484 void console_handler::do_set_var() {
03485     const std::string data = get_data();
03486     if (data.empty()) {
03487         command_failed_need_arg(1);
03488         return;
03489     }
03490     const std::string::const_iterator j = std::find(data.begin(),data.end(),'=');
03491     if(j != data.end()) {
03492         const std::string name(data.begin(),j);
03493         const std::string value(j+1,data.end());
03494         menu_handler_.gamestate_.set_variable(name,value);
03495     } else {
03496         command_failed(_("Variable not found"));
03497     }
03498 }
03499 void console_handler::do_show_var() {
03500     gui2::show_transient_message((*menu_handler_.gui_).video(),"",menu_handler_.gamestate_.get_variable(get_data()));
03501 }
03502 
03503 
03504 void console_handler::do_inspect() {
03505     vconfig cfg = vconfig::empty_vconfig();
03506     gui2::tgamestate_inspector inspect_dialog(cfg);
03507     inspect_dialog.show(resources::screen->video());
03508 }
03509 
03510 void console_handler::do_control_dialog()
03511 {
03512     gui2::tmp_change_control mp_change_control(&menu_handler_);
03513     mp_change_control.show(resources::screen->video());
03514 }
03515 
03516 void console_handler::do_manage() {
03517     config cfg;
03518     gui2::tdata_manage manager(cfg);
03519     manager.show(resources::screen->video());
03520 }
03521 
03522 void console_handler::do_unit() {
03523     // prevent SIGSEGV due to attempt to set HP during a fight
03524     if (events::commands_disabled > 0)
03525         return;
03526     unit_map::iterator i = menu_handler_.current_unit();
03527     if (i == menu_handler_.units_.end()) return;
03528     const map_location loc = i->get_location();
03529     const std::string data = get_data(1);
03530     std::vector<std::string> parameters = utils::split(data, '=', utils::STRIP_SPACES);
03531     if (parameters.size() < 2)
03532         return;
03533 
03534     const std::string& name = parameters[0];
03535     const std::string& value = parameters[1];
03536 
03537     // FIXME: Avoids a core dump on display
03538     // because alignment strings get reduced
03539     // to an enum, then used to index an
03540     // array of strings.
03541     // But someday the code ought to be
03542     // changed to allow general string
03543     // alignments for UMC.
03544     if (name == "alignment" && (value != "lawful" && value != "neutral" && value != "chaotic" && value != "liminal")) {
03545         utils::string_map symbols;
03546         symbols["alignment"] = get_arg(1);
03547         command_failed(VGETTEXT("Invalid alignment: '$alignment',"
03548             " needs to be one of lawful, neutral, chaotic, or liminal.", symbols));
03549         return;
03550     }
03551     if (name == "advances" ){
03552         int int_value = lexical_cast<int>(value);
03553         for (int levels=0; levels<int_value; levels++) {
03554             i->set_experience(i->max_experience());
03555             dialogs::advance_unit(loc);
03556             i = menu_handler_.units_.find(loc);
03557             if (!i.valid()) {
03558                 break;
03559             }
03560         }
03561     } else {
03562         config cfg;
03563         i->write(cfg);
03564         menu_handler_.units_.erase(loc);
03565         cfg[name] = value;
03566         unit new_u(cfg, true);
03567         menu_handler_.units_.add(loc, new_u);
03568     }
03569     if (name == "fail") { //testcase for bug #18488
03570         assert(i.valid());
03571     }
03572     menu_handler_.gui_->invalidate(loc);
03573     menu_handler_.gui_->invalidate_unit();
03574 }
03575 /*void console_handler::do_buff() {
03576     print(get_cmd(), _("Debug mode activated!"));
03577     const unit_map::iterator i = menu_handler_.current_unit();
03578     if(i != menu_handler_.units_.end()) {
03579         //i->second.add_trait(get_data());
03580         menu_handler_.gui_->invalidate(i->first);
03581         menu_handler_.gui_->invalidate_unit();
03582     } else {
03583         command_failed("No unit selected");
03584     }
03585 }
03586 void console_handler::do_unbuff() {
03587     const unit_map::iterator i = menu_handler_.current_unit();
03588     if(i != menu_handler_.units_.end()) {
03589         // FIXME: 'data_' is the trait.  Clear it.
03590 
03591         menu_handler_.gui_->invalidate(i->first);
03592         menu_handler_.gui_->invalidate_unit();
03593     } else {
03594         command_failed(_("No unit selected"));
03595     }
03596 }*/
03597 void console_handler::do_discover() {
03598     foreach (const unit_type_data::unit_type_map::value_type &i, unit_types.types()) {
03599         preferences::encountered_units().insert(i.second.id());
03600     }
03601 }
03602 void console_handler::do_undiscover() {
03603     const int res = gui2::show_message((*menu_handler_.gui_).video(), "Undiscover", _("Do you wish to clear all of your discovered units from help?"), gui2::tmessage::yes_no_buttons);
03604     if(res != gui2::twindow::CANCEL) {
03605         preferences::encountered_units().clear();
03606     }
03607 }
03608 void console_handler::do_create() {
03609     const mouse_handler& mousehandler = resources::controller->get_mouse_handler_base();
03610     const map_location &loc = mousehandler.get_last_hex();
03611     if (menu_handler_.map_.on_board(loc)) {
03612         const unit_type *ut = unit_types.find(get_data());
03613         if (!ut) {
03614             command_failed(_("Invalid unit type"));
03615             return;
03616         }
03617 
03618         menu_handler_.units_.erase(loc);
03619 
03620         unit created(ut, 1, true);
03621         created.new_turn();
03622 
03623         menu_handler_.units_.add(loc, created);
03624         menu_handler_.gui_->invalidate(loc);
03625         menu_handler_.gui_->invalidate_unit();
03626     } else {
03627         command_failed(_("Invalid location"));
03628     }
03629 }
03630 void console_handler::do_fog() {
03631     menu_handler_.teams_[team_num_ - 1].set_fog( !menu_handler_.teams_[team_num_ - 1].uses_fog() );
03632     recalculate_fog(team_num_);
03633     menu_handler_.gui_->recalculate_minimap();
03634     menu_handler_.gui_->redraw_everything();
03635 }
03636 void console_handler::do_shroud() {
03637     menu_handler_.teams_[team_num_ - 1].set_shroud( !menu_handler_.teams_[team_num_ - 1].uses_shroud() );
03638     menu_handler_.clear_shroud(team_num_);
03639     menu_handler_.gui_->recalculate_minimap();
03640     menu_handler_.gui_->redraw_everything();
03641 }
03642 void console_handler::do_gold() {
03643     menu_handler_.teams_[team_num_ - 1].spend_gold(-lexical_cast_default<int>(get_data(),1000));
03644     menu_handler_.gui_->redraw_everything();
03645 }
03646 void console_handler::do_event() {
03647     game_events::fire(get_data());
03648     menu_handler_.gui_->redraw_everything();
03649 }
03650 void console_handler::do_toggle_draw_coordinates() {
03651     menu_handler_.gui_->set_draw_coordinates(!menu_handler_.gui_->get_draw_coordinates());
03652     menu_handler_.gui_->invalidate_all();
03653 }
03654 void console_handler::do_toggle_draw_terrain_codes() {
03655     menu_handler_.gui_->set_draw_terrain_codes(!menu_handler_.gui_->get_draw_terrain_codes());
03656     menu_handler_.gui_->invalidate_all();
03657 }
03658 
03659 void console_handler::do_toggle_whiteboard() {
03660     resources::whiteboard->set_active(!resources::whiteboard->is_active());
03661     if (resources::whiteboard->is_active()) {
03662         print(get_cmd(), _("Planning mode activated!"));
03663         resources::whiteboard->print_help_once();
03664     } else {
03665         print(get_cmd(), _("Planning mode deactivated!"));
03666     }
03667 }
03668 
03669 void console_handler::do_whiteboard_options()
03670 {
03671     resources::whiteboard->options_dlg();
03672 }
03673 
03674 void menu_handler::do_ai_formula(const std::string& str,
03675     int side_num, mouse_handler& /*mousehandler*/)
03676 {
03677     try {
03678         add_chat_message(time(NULL), _("ai"), 0, ai::manager::evaluate_command(side_num, str));
03679     } catch(end_turn_exception&) {
03680         resources::controller->force_end_turn();
03681     } catch(...) {
03682         //add_chat_message(time(NULL), _("ai"), 0, "ERROR IN FORMULA");
03683     }
03684 }
03685 
03686 void menu_handler::user_command()
03687 {
03688     textbox_info_.show(gui::TEXTBOX_COMMAND,sgettext("prompt^Command:"), "", false, *gui_);
03689 }
03690 
03691 void menu_handler::request_control_change ( int side_num, const std::string& player )
03692 {
03693     std::string side = str_cast(side_num);
03694     //if this is our side we are always allowed to change the controller
03695     if (teams_[side_num - 1].is_human()) {
03696         if (player == preferences::login())
03697             return;
03698         change_side_controller(side,player);
03699     } else {
03700         //it is not our side, the server will decide if we can change the
03701         //controller (that is if we are host of the game)
03702         change_side_controller(side,player);
03703     }
03704 }
03705 
03706 void menu_handler::custom_command()
03707 {
03708     std::vector<std::string> commands = utils::split(preferences::custom_command(), ';');
03709     std::vector<std::string>::iterator c = commands.begin();
03710     for (; c != commands.end() ; ++c) {
03711         do_command(*c);
03712     }
03713 }
03714 
03715 void menu_handler::ai_formula()
03716 {
03717             if (network::nconnections() == 0) {
03718                 textbox_info_.show(gui::TEXTBOX_AI,sgettext("prompt^Command:"), "", false, *gui_);
03719     }
03720 }
03721 
03722 void menu_handler::clear_messages()
03723 {
03724     gui_->clear_chat_messages();    // also clear debug-messages and WML-error-messages
03725 }
03726 
03727 void menu_handler::change_controller(const std::string& side, const std::string& controller)
03728 {
03729     config cfg;
03730     config& change = cfg.add_child("change_controller");
03731     change["side"] = side;
03732     change["controller"] = controller;
03733     network::send_data(cfg, 0);
03734 }
03735 
03736 void menu_handler::change_side_controller(const std::string& side, const std::string& player)
03737 {
03738     config cfg;
03739     config& change = cfg.add_child("change_controller");
03740     change["side"] = side;
03741     change["player"] = player;
03742 
03743     network::send_data(cfg, 0);
03744 }
03745 } // end namespace events
03746 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines

Generated by doxygen 1.7.1 on Fri May 25 2012 01:03:05 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs