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