play_controller.cpp

Go to the documentation of this file.
00001 /* $Id: play_controller.cpp 54211 2012-05-18 21:45:50Z ai0867 $ */
00002 /*
00003    Copyright (C) 2006 - 2012 by Joerg Hinrichs <joerg.hinrichs@alice-dsl.de>
00004    wesnoth playlevel Copyright (C) 2003 by David White <dave@whitevine.net>
00005    Part of the Battle for Wesnoth Project http://www.wesnoth.org/
00006 
00007    This program is free software; you can redistribute it and/or modify
00008    it under the terms of the GNU General Public License as published by
00009    the Free Software Foundation; either version 2 of the License, or
00010    (at your option) any later version.
00011    This program is distributed in the hope that it will be useful,
00012    but WITHOUT ANY WARRANTY.
00013 
00014    See the COPYING file for more details.
00015 */
00016 
00017 /**
00018  *  @file
00019  *  Handle input via mouse & keyboard, events, schedule commands.
00020  */
00021 
00022 #include "play_controller.hpp"
00023 #include "dialogs.hpp"
00024 #include "foreach.hpp"
00025 #include "game_events.hpp"
00026 #include "gettext.hpp"
00027 #include "halo.hpp"
00028 #include "loadscreen.hpp"
00029 #include "log.hpp"
00030 #include "pathfind/teleport.hpp"
00031 #include "resources.hpp"
00032 #include "savegame.hpp"
00033 #include "sound.hpp"
00034 #include "unit_id.hpp"
00035 #include "terrain_filter.hpp"
00036 #include "save_blocker.hpp"
00037 #include "preferences_display.hpp"
00038 #include "replay.hpp"
00039 #include "soundsource.hpp"
00040 #include "tooltips.hpp"
00041 #include "game_preferences.hpp"
00042 #include "wml_exception.hpp"
00043 #include "formula_string_utils.hpp"
00044 #include "ai/manager.hpp"
00045 #include "ai/testing.hpp"
00046 #include "whiteboard/manager.hpp"
00047 #include "scripting/lua.hpp"
00048 
00049 static lg::log_domain log_engine("engine");
00050 #define LOG_NG LOG_STREAM(info, log_engine)
00051 #define DBG_NG LOG_STREAM(debug, log_engine)
00052 
00053 static lg::log_domain log_display("display");
00054 #define ERR_DP LOG_STREAM(err, log_display)
00055 
00056 static void clear_resources()
00057 {
00058     resources::game_map = NULL;
00059     resources::units = NULL;
00060     resources::teams = NULL;
00061     resources::state_of_game = NULL;
00062     resources::controller = NULL;
00063     resources::screen = NULL;
00064     resources::soundsources = NULL;
00065     resources::tod_manager = NULL;
00066     resources::whiteboard = NULL;
00067     resources::persist = NULL;
00068 }
00069 
00070 play_controller::play_controller(const config& level, game_state& state_of_game,
00071         int ticks, int num_turns, const config& game_config, CVideo& video,
00072         bool skip_replay) :
00073     controller_base(ticks, game_config, video),
00074     observer(),
00075     savegame_config(),
00076     animation_cache(),
00077     prefs_disp_manager_(),
00078     tooltips_manager_(),
00079     events_manager_(),
00080     halo_manager_(),
00081     labels_manager_(),
00082     help_manager_(&game_config, &map_),
00083     mouse_handler_(NULL, teams_, units_, map_, tod_manager_),
00084     menu_handler_(NULL, units_, teams_, level, map_, game_config, tod_manager_, state_of_game),
00085     soundsources_manager_(),
00086     tod_manager_(level, num_turns),
00087     pathfind_manager_(),
00088     persist_(),
00089     gui_(),
00090     statistics_context_(level["name"]),
00091     level_(level),
00092     teams_(),
00093     gamestate_(state_of_game),
00094     map_(game_config, level),
00095     units_(),
00096     undo_stack_(),
00097     redo_stack_(),
00098     whiteboard_manager_(),
00099     xp_mod_(level["experience_modifier"].to_int(100)),
00100     loading_game_(level["playing_team"].empty() == false),
00101     first_human_team_(-1),
00102     player_number_(1),
00103     first_player_(level_["playing_team"].to_int() + 1),
00104     start_turn_(tod_manager_.turn()), // tod_manager_ constructed above
00105     is_host_(true),
00106     skip_replay_(skip_replay),
00107     linger_(false),
00108     it_is_a_new_turn_(true),
00109     init_side_done_(false),
00110     savenames_(),
00111     wml_commands_(),
00112     victory_when_enemies_defeated_(true),
00113     end_level_data_(),
00114     victory_music_(),
00115     defeat_music_()
00116 {
00117     resources::game_map = &map_;
00118     resources::units = &units_;
00119     resources::teams = &teams_;
00120     resources::state_of_game = &gamestate_;
00121     resources::controller = this;
00122     resources::tod_manager = &tod_manager_;
00123     resources::undo_stack = &undo_stack_;
00124     resources::redo_stack = &redo_stack_;
00125     resources::persist = &persist_;
00126     persist_.start_transaction();
00127 
00128     // Setup victory and defeat music
00129     set_victory_music_list(level_["victory_music"]);
00130     set_defeat_music_list(level_["defeat_music"]);
00131 
00132     game_config::add_color_info(level);
00133     hotkey::deactivate_all_scopes();
00134     hotkey::set_scope_active(hotkey::SCOPE_GENERAL);
00135     hotkey::set_scope_active(hotkey::SCOPE_GAME);
00136     try {
00137         init(video);
00138     } catch (...) {
00139         clear_resources();
00140         throw;
00141     }
00142 }
00143 
00144 play_controller::~play_controller()
00145 {
00146     clear_resources();
00147     resources::tunnels = NULL;
00148 }
00149 
00150 void play_controller::init(CVideo& video){
00151     util::scoped_resource<loadscreen::global_loadscreen_manager*, util::delete_item> scoped_loadscreen_manager;
00152     loadscreen::global_loadscreen_manager* loadscreen_manager = loadscreen::global_loadscreen_manager::get();
00153     if (!loadscreen_manager)
00154     {
00155         scoped_loadscreen_manager.assign(new loadscreen::global_loadscreen_manager(video));
00156         loadscreen_manager = scoped_loadscreen_manager.get();
00157     }
00158 
00159     loadscreen::start_stage("load level");
00160     // If the recorder has no event, adds an "game start" event
00161     // to the recorder, whose only goal is to initialize the RNG
00162     if(recorder.empty()) {
00163         recorder.add_start();
00164     } else {
00165         recorder.pre_replay();
00166     }
00167     recorder.set_skip(false);
00168 
00169     bool snapshot = level_["snapshot"].to_bool();
00170 
00171     if (level_["modify_placing"].to_bool()) {
00172         LOG_NG << "modifying placing...\n";
00173         place_sides_in_preferred_locations();
00174     }
00175 
00176     foreach (const config &t, level_.child_range("time_area")) {
00177         tod_manager_.add_time_area(t);
00178     }
00179 
00180     LOG_NG << "initialized teams... "    << (SDL_GetTicks() - ticks_) << "\n";
00181     loadscreen::start_stage("init teams");
00182 
00183     resources::teams->resize(level_.child_count("side"));
00184 
00185     // This *needs* to be created before the show_intro and show_map_scene
00186     // as that functions use the manager state_of_game
00187     // Has to be done before registering any events!
00188     events_manager_.reset(new game_events::manager(level_));
00189 
00190     std::set<std::string> seen_save_ids;
00191 
00192     std::vector<team_builder_ptr> team_builders;
00193 
00194     int team_num = 0;
00195     foreach (const config &side, level_.child_range("side"))
00196     {
00197         std::string save_id = get_unique_saveid(side, seen_save_ids);
00198         seen_save_ids.insert(save_id);
00199         if (first_human_team_ == -1) {
00200             const std::string &controller = side["controller"];
00201             if (controller == preferences::client_type() &&
00202                 side["id"] == preferences::login()) {
00203                 first_human_team_ = team_num;
00204             } else if (controller == "human") {
00205                 first_human_team_ = team_num;
00206             }
00207         }
00208         team_builder_ptr tb_ptr = gamestate_.create_team_builder(side,
00209             save_id, teams_, level_, map_, units_, snapshot);
00210         ++team_num;
00211         gamestate_.build_team_stage_one(tb_ptr);
00212         team_builders.push_back(tb_ptr);
00213     }
00214 
00215     foreach (team_builder_ptr tb_ptr, team_builders)
00216     {
00217         gamestate_.build_team_stage_two(tb_ptr);
00218     }
00219 
00220     // mouse_handler expects at least one team for linger mode to work.
00221     if (teams_.empty()) end_level_data_.linger_mode = false;
00222 
00223     LOG_NG << "loading units..." << (SDL_GetTicks() - ticks_) << "\n";
00224     loadscreen::start_stage("load units");
00225     preferences::encounter_recruitable_units(teams_);
00226     preferences::encounter_start_units(units_);
00227     preferences::encounter_recallable_units(teams_);
00228     preferences::encounter_map_terrain(map_);
00229 
00230 
00231     LOG_NG << "initializing theme... " << (SDL_GetTicks() - ticks_) << '\n';
00232     loadscreen::start_stage("init theme");
00233     const config &theme_cfg = get_theme(game_config_, level_["theme"]);
00234 
00235     LOG_NG << "building terrain rules... " << (SDL_GetTicks() - ticks_) << '\n';
00236     loadscreen::start_stage("build terrain");
00237     gui_.reset(new game_display(units_, video, map_, tod_manager_, teams_, theme_cfg, level_));
00238     if (!gui_->video().faked()) {
00239         if (gamestate_.mp_settings().mp_countdown)
00240             gui_->get_theme().modify_label("time-icon", _ ("time left for current turn"));
00241         else
00242             gui_->get_theme().modify_label("time-icon", _ ("current local time"));
00243     }
00244 
00245     loadscreen::start_stage("init display");
00246     mouse_handler_.set_gui(gui_.get());
00247     menu_handler_.set_gui(gui_.get());
00248     resources::screen = gui_.get();
00249 
00250     LOG_NG << "done initializing display... " << (SDL_GetTicks() - ticks_) << "\n";
00251 
00252     if(first_human_team_ != -1) {
00253         gui_->set_team(first_human_team_);
00254     }
00255     else if (is_observer())
00256     {
00257         // Find first team that is allowed to be observered.
00258         // If not set here observer would be without fog untill
00259         // the first turn of observerable side
00260         size_t i;
00261         for (i=0;i < teams_.size();++i)
00262         {
00263             if (!teams_[i].get_disallow_observers())
00264             {
00265                 gui_->set_team(i);
00266             }
00267         }
00268     }
00269 
00270     browse_ = true;
00271 
00272     init_managers();
00273     // add era events for MP game
00274     if (const config &era_cfg = level_.child("era")) {
00275         game_events::add_events(era_cfg.child_range("event"), "era_events");
00276     }
00277 
00278     loadscreen::global_loadscreen->start_stage("start game");
00279     loadscreen_manager->reset();
00280 }
00281 
00282 void play_controller::init_managers(){
00283     LOG_NG << "initializing managers... " << (SDL_GetTicks() - ticks_) << "\n";
00284     prefs_disp_manager_.reset(new preferences::display_manager(gui_.get()));
00285     tooltips_manager_.reset(new tooltips::manager(gui_->video()));
00286     soundsources_manager_.reset(new soundsource::manager(*gui_));
00287     pathfind_manager_.reset(new pathfind::manager(level_));
00288     whiteboard_manager_.reset(new wb::manager());
00289 
00290     resources::soundsources = soundsources_manager_.get();
00291     resources::tunnels = pathfind_manager_.get();
00292     resources::whiteboard = whiteboard_manager_.get();
00293 
00294     halo_manager_.reset(new halo::manager(*gui_));
00295     LOG_NG << "done initializing managers... " << (SDL_GetTicks() - ticks_) << "\n";
00296 }
00297 
00298 static int placing_score(const config& side, const gamemap& map, const map_location& pos)
00299 {
00300     int positions = 0, liked = 0;
00301     const t_translation::t_list terrain = t_translation::read_list(side["terrain_liked"]);
00302 
00303     for(int i = pos.x-8; i != pos.x+8; ++i) {
00304         for(int j = pos.y-8; j != pos.y+8; ++j) {
00305             const map_location pos(i,j);
00306             if(map.on_board(pos)) {
00307                 ++positions;
00308                 if(std::count(terrain.begin(),terrain.end(),map[pos])) {
00309                     ++liked;
00310                 }
00311             }
00312         }
00313     }
00314 
00315     return (100*liked)/positions;
00316 }
00317 
00318 struct placing_info {
00319 
00320     placing_info() :
00321         side(0),
00322         score(0),
00323         pos()
00324     {
00325     }
00326 
00327     int side, score;
00328     map_location pos;
00329 };
00330 
00331 static bool operator<(const placing_info& a, const placing_info& b) { return a.score > b.score; }
00332 
00333 void play_controller::place_sides_in_preferred_locations()
00334 {
00335     std::vector<placing_info> placings;
00336 
00337     int num_pos = map_.num_valid_starting_positions();
00338 
00339     int side_num = 1;
00340     foreach (const config &side, level_.child_range("side"))
00341     {
00342         for(int p = 1; p <= num_pos; ++p) {
00343             const map_location& pos = map_.starting_position(p);
00344             int score = placing_score(side, map_, pos);
00345             placing_info obj;
00346             obj.side = side_num;
00347             obj.score = score;
00348             obj.pos = pos;
00349             placings.push_back(obj);
00350         }
00351         ++side_num;
00352     }
00353 
00354     std::sort(placings.begin(),placings.end());
00355     std::set<int> placed;
00356     std::set<map_location> positions_taken;
00357 
00358     for (std::vector<placing_info>::const_iterator i = placings.begin(); i != placings.end() && int(placed.size()) != side_num - 1; ++i) {
00359         if(placed.count(i->side) == 0 && positions_taken.count(i->pos) == 0) {
00360             placed.insert(i->side);
00361             positions_taken.insert(i->pos);
00362             map_.set_starting_position(i->side,i->pos);
00363             LOG_NG << "placing side " << i->side << " at " << i->pos << '\n';
00364         }
00365     }
00366 }
00367 
00368 void play_controller::objectives(){
00369     menu_handler_.objectives(gui_->viewing_team()+1);
00370 }
00371 
00372 void play_controller::show_statistics(){
00373     menu_handler_.show_statistics(gui_->viewing_team()+1);
00374 }
00375 
00376 void play_controller::unit_list(){
00377     menu_handler_.unit_list();
00378 }
00379 
00380 void play_controller::status_table(){
00381     menu_handler_.status_table();
00382 }
00383 
00384 void play_controller::save_game(){
00385     if(save_blocker::try_block()) {
00386         save_blocker::save_unblocker unblocker;
00387         savegame::game_savegame save(gamestate_, *gui_, to_config(), preferences::compress_saves());
00388         save.save_game_interactive(gui_->video(), "", gui::OK_CANCEL);
00389     } else {
00390         save_blocker::on_unblock(this,&play_controller::save_game);
00391     }
00392 }
00393 
00394 void play_controller::save_replay(){
00395     if(save_blocker::try_block()) {
00396         save_blocker::save_unblocker unblocker;
00397         savegame::replay_savegame save(gamestate_, preferences::compress_saves());
00398         save.save_game_interactive(gui_->video(), "", gui::OK_CANCEL);
00399     } else {
00400         save_blocker::on_unblock(this,&play_controller::save_replay);
00401     }
00402 }
00403 
00404 void play_controller::save_map(){
00405     if(save_blocker::try_block()) {
00406         save_blocker::save_unblocker unblocker;
00407         menu_handler_.save_map();
00408     } else {
00409         save_blocker::on_unblock(this,&play_controller::save_map);
00410     }
00411 }
00412 
00413 void play_controller::load_game(){
00414     savegame::loadgame load(*gui_, game_config_, gamestate_);
00415     load.load_game();
00416 }
00417 
00418 void play_controller::preferences(){
00419     menu_handler_.preferences();
00420 }
00421 
00422 void play_controller::left_mouse_click(){
00423     int x = gui_->get_location_x(gui_->mouseover_hex());
00424     int y = gui_->get_location_y(gui_->mouseover_hex());
00425 
00426     SDL_MouseButtonEvent event;
00427 
00428     event.button = 1;
00429     event.x = x + 30;
00430     event.y = y + 30;
00431     event.which = 0;
00432     event.state = SDL_PRESSED;
00433 
00434     mouse_handler_.mouse_press(event, false);
00435 }
00436 
00437 void play_controller::right_mouse_click(){
00438     int x = gui_->get_location_x(gui_->mouseover_hex());
00439     int y = gui_->get_location_y(gui_->mouseover_hex());
00440 
00441     SDL_MouseButtonEvent event;
00442 
00443     event.button = 3;
00444     event.x = x + 30;
00445     event.y = y + 30;
00446     event.which = 0;
00447     event.state = SDL_PRESSED;
00448 
00449     mouse_handler_.mouse_press(event, true);
00450 }
00451 
00452 
00453 void play_controller::cycle_units(){
00454     mouse_handler_.cycle_units(browse_);
00455 }
00456 
00457 void play_controller::cycle_back_units(){
00458     mouse_handler_.cycle_back_units(browse_);
00459 }
00460 
00461 void play_controller::show_chat_log(){
00462     menu_handler_.show_chat_log();
00463 }
00464 
00465 void play_controller::show_help(){
00466     menu_handler_.show_help();
00467 }
00468 
00469 void play_controller::undo(){
00470     // deselect unit (only here, not to be done when undoing attack-move)
00471     mouse_handler_.deselect_hex();
00472     menu_handler_.undo(player_number_);
00473 }
00474 
00475 void play_controller::redo(){
00476     // deselect unit (only here, not to be done when undoing attack-move)
00477     mouse_handler_.deselect_hex();
00478     menu_handler_.redo(player_number_);
00479 }
00480 
00481 void play_controller::show_enemy_moves(bool ignore_units){
00482     menu_handler_.show_enemy_moves(ignore_units, player_number_);
00483 }
00484 
00485 void play_controller::goto_leader(){
00486     menu_handler_.goto_leader(player_number_);
00487 }
00488 
00489 void play_controller::unit_description(){
00490     menu_handler_.unit_description();
00491 }
00492 
00493 void play_controller::toggle_ellipses(){
00494     menu_handler_.toggle_ellipses();
00495 }
00496 
00497 void play_controller::toggle_grid(){
00498     menu_handler_.toggle_grid();
00499 }
00500 
00501 void play_controller::search(){
00502     menu_handler_.search();
00503 }
00504 
00505 void play_controller::fire_prestart(bool execute)
00506 {
00507     // Run initialization scripts, even if loading from a snapshot.
00508     resources::state_of_game->set_phase(game_state::PRELOAD);
00509     resources::lua_kernel->initialize();
00510     game_events::fire("preload");
00511 
00512     // pre-start events must be executed before any GUI operation,
00513     // as those may cause the display to be refreshed.
00514     if (execute){
00515         update_locker lock_display(gui_->video());
00516         resources::state_of_game->set_phase(game_state::PRESTART);
00517         game_events::fire("prestart");
00518         check_end_level();
00519         // prestart event may modify start turn with WML, reflect any changes.
00520         start_turn_ = turn();
00521     }
00522 }
00523 
00524 void play_controller::fire_start(bool execute){
00525     if(execute) {
00526         resources::state_of_game->set_phase(game_state::START);
00527         game_events::fire("start");
00528         check_end_level();
00529         // start event may modify start turn with WML, reflect any changes.
00530         start_turn_ = turn();
00531         gamestate_.get_variable("turn_number") = int(start_turn_);
00532     } else {
00533         it_is_a_new_turn_ = false;
00534     }
00535     resources::state_of_game->set_phase(game_state::PLAY);
00536 }
00537 
00538 void play_controller::init_gui(){
00539     gui_->begin_game();
00540     gui_->update_tod();
00541 
00542     for(std::vector<team>::iterator t = teams_.begin(); t != teams_.end(); ++t) {
00543         clear_shroud(t - teams_.begin() + 1);
00544     }
00545 }
00546 
00547 void play_controller::init_side(const unsigned int team_index, bool is_replay){
00548     log_scope("player turn");
00549     init_side_done_ = false;
00550 
00551     mouse_handler_.set_side(team_index + 1);
00552 
00553     // If we are observers we move to watch next team if it is allowed
00554     if (is_observer() && !current_team().get_disallow_observers()) {
00555         gui_->set_team(size_t(team_index));
00556     }
00557     gui_->set_playing_team(size_t(team_index));
00558 
00559     gamestate_.get_variable("side_number") = player_number_;
00560     gamestate_.last_selected = map_location::null_location;
00561 
00562     maybe_do_init_side(team_index, is_replay);
00563 }
00564 
00565 /**
00566  * Called by turn_info::process_network_data() or init_side() to call do_init_side() if necessary.
00567  */
00568 void play_controller::maybe_do_init_side(const unsigned int team_index, bool is_replay) {
00569     /**
00570      * We do side init only if not done yet for a local side when we are not replaying.
00571      * For all other sides it is recorded in replay and replay handler has to handle
00572      * calling do_init_side() functions.
00573      **/
00574     if (is_replay || init_side_done_ || !current_team().is_local()) {
00575         return;
00576     }
00577 
00578     if (!loading_game_) recorder.init_side();
00579 
00580     do_init_side(team_index, is_replay);
00581 }
00582 
00583 /**
00584  * Called by replay handler or init_side() to do actual work for turn change.
00585  */
00586 void play_controller::do_init_side(const unsigned int team_index, bool is_replay) {
00587     log_scope("player turn");
00588     team& current_team = teams_[team_index];
00589 
00590     const std::string turn_num = str_cast(turn());
00591     const std::string side_num = str_cast(team_index + 1);
00592 
00593     // If this is right after loading a game we don't need to fire events and such. It was already done before saving.
00594     if (!loading_game_) {
00595         if(it_is_a_new_turn_)
00596         {
00597             game_events::fire("turn " + turn_num);
00598             game_events::fire("new turn");
00599             it_is_a_new_turn_ = false;
00600         }
00601 
00602         game_events::fire("side turn");
00603         game_events::fire("side " + side_num + " turn");
00604         game_events::fire("side turn " + turn_num);
00605         game_events::fire("side " + side_num + " turn " + turn_num);
00606     }
00607 
00608     if(current_team.is_human() && !is_replay) {
00609         update_gui_to_player(player_number_ - 1);
00610     }
00611     // We want to work out if units for this player should get healed,
00612     // and the player should get income now.
00613     // Healing/income happen if it's not the first turn of processing,
00614     // or if we are loading a game.
00615     if (!loading_game_ && turn() > 1) {
00616         for(unit_map::iterator i = units_.begin(); i != units_.end(); ++i) {
00617             if (i->side() == player_number_) {
00618                 i->new_turn();
00619             }
00620         }
00621 
00622         current_team.new_turn();
00623 
00624         // If the expense is less than the number of villages owned
00625         // times the village support capacity,
00626         // then we don't have to pay anything at all
00627         int expense = side_upkeep(player_number_) -
00628             current_team.support();
00629         if(expense > 0) {
00630             current_team.spend_gold(expense);
00631         }
00632 
00633         calculate_healing(player_number_, !skip_replay_);
00634         reset_resting(units_, player_number_);
00635     }
00636 
00637     if (!loading_game_) {
00638         game_events::fire("turn refresh");
00639         game_events::fire("side " + side_num + " turn refresh");
00640         game_events::fire("turn " + turn_num + " refresh");
00641         game_events::fire("side " + side_num + " turn " + turn_num + " refresh");
00642     }
00643 
00644     const time_of_day &tod = tod_manager_.get_time_of_day();
00645 
00646     if (int(team_index) + 1 == first_player_)
00647         sound::play_sound(tod.sounds, sound::SOUND_SOURCES);
00648 
00649     if (!recorder.is_skipping()){
00650         clear_shroud(team_index + 1, !loading_game_);
00651         gui_->invalidate_all();
00652     }
00653 
00654     if (!recorder.is_skipping() && !skip_replay_ && current_team.get_scroll_to_leader()){
00655         gui_->scroll_to_leader(units_, player_number_,game_display::ONSCREEN,false);
00656     }
00657     loading_game_ = false;
00658     init_side_done_ = true;
00659 
00660     resources::whiteboard->on_init_side();
00661 }
00662 
00663 //builds the snapshot config from its members and their configs respectively
00664 config play_controller::to_config() const
00665 {
00666     config cfg;
00667 
00668     cfg.merge_attributes(level_);
00669 
00670     for(std::vector<team>::const_iterator t = teams_.begin(); t != teams_.end(); ++t) {
00671         int side_num = t - teams_.begin() + 1;
00672 
00673         config& side = cfg.add_child("side");
00674         t->write(side);
00675         side["no_leader"] = true;
00676         side["side"] = str_cast(side_num);
00677 
00678         if (!linger_){
00679             //current visible units
00680             for(unit_map::const_iterator i = units_.begin(); i != units_.end(); ++i) {
00681                 if (i->side() == side_num) {
00682                     config& u = side.add_child("unit");
00683                     i->get_location().write(u);
00684                     i->write(u);
00685                 }
00686             }
00687         }
00688         //recall list
00689         {
00690             for(std::vector<unit>::const_iterator j = t->recall_list().begin();
00691                 j != t->recall_list().end(); ++j) {
00692                     config& u = side.add_child("unit");
00693                     j->write(u);
00694             }
00695         }
00696     }
00697 
00698     cfg.merge_with(tod_manager_.to_config());
00699 
00700     if(linger_) {
00701         config endlevel;
00702         end_level_data_.write(endlevel);
00703         cfg.add_child("endlevel", endlevel);
00704     }
00705 
00706     // Write terrain_graphics data in snapshot, too
00707     foreach (const config &tg, level_.child_range("terrain_graphics")) {
00708         cfg.add_child("terrain_graphics", tg);
00709     }
00710 
00711     //write out the current state of the map
00712 
00713     config& map = cfg.add_child("map");
00714     map_.write(map);
00715 
00716     cfg.merge_with(pathfind_manager_->to_config());
00717 
00718     config display;
00719     gui_->write(display);
00720     cfg.add_child("display", display);
00721 
00722     return cfg;
00723 }
00724 
00725 void play_controller::finish_side_turn(){
00726 
00727     resources::whiteboard->on_finish_side_turn(player_number_);
00728 
00729     for(unit_map::iterator uit = units_.begin(); uit != units_.end(); ++uit) {
00730         if (uit->side() == player_number_)
00731             uit->end_turn();
00732     }
00733 
00734     const std::string turn_num = str_cast(turn());
00735     const std::string side_num = str_cast(player_number_);
00736     game_events::fire("side turn end");
00737     game_events::fire("side "+ side_num + " turn end");
00738     game_events::fire("side turn " + turn_num + " end");
00739     game_events::fire("side " + side_num + " turn " + turn_num + " end");
00740 
00741     // This implements "delayed map sharing."
00742     // It is meant as an alternative to shared vision.
00743     if(current_team().copy_ally_shroud()) {
00744         gui_->recalculate_minimap();
00745         gui_->invalidate_all();
00746     }
00747 
00748     mouse_handler_.deselect_hex();
00749     n_unit::id_manager::instance().reset_fake();
00750     game_events::pump();
00751 }
00752 
00753 void play_controller::finish_turn()
00754 {
00755     const std::string turn_num = str_cast(turn());
00756     game_events::fire("turn end");
00757     game_events::fire("turn " + turn_num + " end");
00758 }
00759 
00760 bool play_controller::enemies_visible() const
00761 {
00762     // If we aren't using fog/shroud, this is easy :)
00763     if(current_team().uses_fog() == false && current_team().uses_shroud() == false)
00764         return true;
00765 
00766     // See if any enemies are visible
00767     for(unit_map::const_iterator u = units_.begin(); u != units_.end(); ++u)
00768         if (current_team().is_enemy(u->side()) && !gui_->fogged(u->get_location()))
00769             return true;
00770 
00771     return false;
00772 }
00773 
00774 bool play_controller::execute_command(hotkey::HOTKEY_COMMAND command, int index)
00775 {
00776     if(index >= 0) {
00777         unsigned i = static_cast<unsigned>(index);
00778         if(i < savenames_.size() && !savenames_[i].empty()) {
00779             // Load the game by throwing load_game_exception
00780             throw game::load_game_exception(savenames_[i],false,false,false,"");
00781 
00782         } else if (i < wml_commands_.size() && wml_commands_[i] != NULL) {
00783             if(gamestate_.last_selected.valid() && wml_commands_[i]->needs_select) {
00784                 recorder.add_event("select", gamestate_.last_selected);
00785             }
00786             map_location const& menu_hex = mouse_handler_.get_last_hex();
00787             recorder.add_event(wml_commands_[i]->name, menu_hex);
00788             if(game_events::fire(wml_commands_[i]->name, menu_hex)) {
00789                 // The event has mutated the gamestate
00790                 apply_shroud_changes(undo_stack_, player_number_);
00791                 undo_stack_.clear();
00792             }
00793             return true;
00794         }
00795     }
00796     return command_executor::execute_command(command, index);
00797 }
00798 
00799 bool play_controller::can_execute_command(hotkey::HOTKEY_COMMAND command, int index) const
00800 {
00801     if(index >= 0) {
00802         unsigned i = static_cast<unsigned>(index);
00803         if((i < savenames_.size() && !savenames_[i].empty())
00804         || (i < wml_commands_.size() && wml_commands_[i] != NULL)) {
00805             return true;
00806         }
00807     }
00808     switch(command) {
00809 
00810     // Commands we can always do:
00811     case hotkey::HOTKEY_LEADER:
00812     case hotkey::HOTKEY_CYCLE_UNITS:
00813     case hotkey::HOTKEY_CYCLE_BACK_UNITS:
00814     case hotkey::HOTKEY_ZOOM_IN:
00815     case hotkey::HOTKEY_ZOOM_OUT:
00816     case hotkey::HOTKEY_ZOOM_DEFAULT:
00817     case hotkey::HOTKEY_FULLSCREEN:
00818     case hotkey::HOTKEY_SCREENSHOT:
00819     case hotkey::HOTKEY_MAP_SCREENSHOT:
00820     case hotkey::HOTKEY_ACCELERATED:
00821     case hotkey::HOTKEY_SAVE_MAP:
00822     case hotkey::HOTKEY_TOGGLE_ELLIPSES:
00823     case hotkey::HOTKEY_TOGGLE_GRID:
00824     case hotkey::HOTKEY_MOUSE_SCROLL:
00825     case hotkey::HOTKEY_ANIMATE_MAP:
00826     case hotkey::HOTKEY_STATUS_TABLE:
00827     case hotkey::HOTKEY_MUTE:
00828     case hotkey::HOTKEY_PREFERENCES:
00829     case hotkey::HOTKEY_OBJECTIVES:
00830     case hotkey::HOTKEY_UNIT_LIST:
00831     case hotkey::HOTKEY_STATISTICS:
00832     case hotkey::HOTKEY_QUIT_GAME:
00833     case hotkey::HOTKEY_SEARCH:
00834     case hotkey::HOTKEY_HELP:
00835     case hotkey::HOTKEY_USER_CMD:
00836     case hotkey::HOTKEY_CUSTOM_CMD:
00837     case hotkey::HOTKEY_AI_FORMULA:
00838     case hotkey::HOTKEY_CLEAR_MSG:
00839     case hotkey::HOTKEY_LEFT_MOUSE_CLICK:
00840     case hotkey::HOTKEY_RIGHT_MOUSE_CLICK:
00841         return true;
00842 
00843     // Commands that have some preconditions:
00844     case hotkey::HOTKEY_SAVE_GAME:
00845     case hotkey::HOTKEY_SAVE_REPLAY:
00846         return !events::commands_disabled;
00847 
00848     case hotkey::HOTKEY_SHOW_ENEMY_MOVES:
00849     case hotkey::HOTKEY_BEST_ENEMY_MOVES:
00850         return !linger_ && enemies_visible();
00851 
00852     case hotkey::HOTKEY_LOAD_GAME:
00853         return network::nconnections() == 0; // Can only load games if not in a network game
00854 
00855     case hotkey::HOTKEY_CHAT_LOG:
00856         return network::nconnections() > 0;
00857 
00858     case hotkey::HOTKEY_REDO:
00859         return !linger_ && !redo_stack_.empty() && !events::commands_disabled && !browse_;
00860     case hotkey::HOTKEY_UNDO:
00861         return !linger_ && !undo_stack_.empty() && !events::commands_disabled && !browse_;
00862 
00863     case hotkey::HOTKEY_UNIT_DESCRIPTION:
00864         return menu_handler_.current_unit() != units_.end();
00865 
00866     case hotkey::HOTKEY_RENAME_UNIT:
00867         return !events::commands_disabled &&
00868             menu_handler_.current_unit() != units_.end() &&
00869             !(menu_handler_.current_unit()->unrenamable()) &&
00870             menu_handler_.current_unit()->side() == gui_->viewing_side() &&
00871             teams_[menu_handler_.current_unit()->side() - 1].is_human();
00872 
00873     default:
00874         return false;
00875     }
00876 }
00877 
00878 void play_controller::enter_textbox()
00879 {
00880     if(menu_handler_.get_textbox().active() == false) {
00881         return;
00882     }
00883 
00884     const std::string str = menu_handler_.get_textbox().box()->text();
00885     const unsigned int team_num = player_number_;
00886     events::mouse_handler& mousehandler = mouse_handler_;
00887 
00888     switch(menu_handler_.get_textbox().mode()) {
00889     case gui::TEXTBOX_SEARCH:
00890         menu_handler_.do_search(str);
00891         menu_handler_.get_textbox().close(*gui_);
00892         break;
00893     case gui::TEXTBOX_MESSAGE:
00894         menu_handler_.do_speak();
00895         menu_handler_.get_textbox().close(*gui_);  //need to close that one after executing do_speak() !
00896         break;
00897     case gui::TEXTBOX_COMMAND:
00898         menu_handler_.get_textbox().close(*gui_);
00899         menu_handler_.do_command(str);
00900         break;
00901     case gui::TEXTBOX_AI:
00902         menu_handler_.get_textbox().close(*gui_);
00903         menu_handler_.do_ai_formula(str, team_num, mousehandler);
00904         break;
00905     default:
00906         menu_handler_.get_textbox().close(*gui_);
00907         ERR_DP << "unknown textbox mode\n";
00908     }
00909 
00910 }
00911 
00912 void play_controller::tab()
00913 {
00914     gui::TEXTBOX_MODE mode = menu_handler_.get_textbox().mode();
00915 
00916     std::set<std::string> dictionary;
00917     switch(mode) {
00918     case gui::TEXTBOX_SEARCH:
00919     {
00920         foreach (const unit &u, units_){
00921             const map_location& loc = u.get_location();
00922             if(!gui_->fogged(loc) &&
00923                     !(teams_[gui_->viewing_team()].is_enemy(u.side()) && u.invisible(loc)))
00924                 dictionary.insert(u.name());
00925         }
00926         //TODO List map labels
00927         break;
00928     }
00929     case gui::TEXTBOX_COMMAND:
00930     {
00931         std::vector<std::string> commands = menu_handler_.get_commands_list();
00932         dictionary.insert(commands.begin(), commands.end());
00933         // no break here, we also want player names from the next case
00934     }
00935     case gui::TEXTBOX_MESSAGE:
00936     {
00937         foreach(const team& t, teams_) {
00938             if(!t.is_empty())
00939                 dictionary.insert(t.current_player());
00940         }
00941 
00942         // Add observers
00943         foreach(const std::string& o, gui_->observers()){
00944             dictionary.insert(o);
00945         }
00946         //Exclude own nick from tab-completion.
00947         //NOTE why ?
00948         dictionary.erase(preferences::login());
00949         break;
00950     }
00951 
00952     default:
00953         ERR_DP << "unknown textbox mode\n";
00954     } //switch(mode)
00955 
00956     menu_handler_.get_textbox().tab(dictionary);
00957 }
00958 
00959 
00960 std::string play_controller::get_unique_saveid(const config& cfg, std::set<std::string>& seen_save_ids)
00961 {
00962     std::string save_id = cfg["save_id"];
00963 
00964     if(save_id.empty()) {
00965         save_id = cfg["id"].str();
00966     }
00967 
00968     if(save_id.empty()) {
00969         save_id="Unknown";
00970     }
00971 
00972     // Make sure the 'save_id' is unique
00973     while(seen_save_ids.count(save_id)) {
00974         save_id += "_";
00975     }
00976 
00977     return save_id;
00978 }
00979 
00980 team& play_controller::current_team()
00981 {
00982     assert(player_number_ > 0 && player_number_ <= int(teams_.size()));
00983     return teams_[player_number_-1];
00984 }
00985 
00986 const team& play_controller::current_team() const
00987 {
00988     assert(player_number_ > 0 && player_number_ <= int(teams_.size()));
00989     return teams_[player_number_-1];
00990 }
00991 
00992 int play_controller::find_human_team_before(const size_t team_num) const
00993 {
00994     if (team_num > teams_.size())
00995         return -2;
00996 
00997     int human_side = -2;
00998     for (int i = team_num-2; i > -1; --i) {
00999         if (teams_[i].is_human()) {
01000             human_side = i;
01001             break;
01002         }
01003     }
01004     if (human_side == -2) {
01005         for (size_t i = teams_.size()-1; i > team_num-1; --i) {
01006             if (teams_[i].is_human()) {
01007                 human_side = i;
01008                 break;
01009             }
01010         }
01011     }
01012     return human_side+1;
01013 }
01014 
01015 void play_controller::slice_before_scroll() {
01016     soundsources_manager_->update();
01017 }
01018 
01019 events::mouse_handler& play_controller::get_mouse_handler_base() {
01020     return mouse_handler_;
01021 }
01022 
01023 game_display& play_controller::get_display() {
01024     return *gui_;
01025 }
01026 
01027 bool play_controller::have_keyboard_focus()
01028 {
01029     return !menu_handler_.get_textbox().active();
01030 }
01031 
01032 void play_controller::process_focus_keydown_event(const SDL_Event& event)
01033 {
01034     if(event.key.keysym.sym == SDLK_ESCAPE) {
01035         menu_handler_.get_textbox().close(*gui_);
01036     } else if(event.key.keysym.sym == SDLK_TAB) {
01037         tab();
01038     } else if(event.key.keysym.sym == SDLK_RETURN || event.key.keysym.sym == SDLK_KP_ENTER) {
01039         enter_textbox();
01040     }
01041 }
01042 
01043 void play_controller::process_keydown_event(const SDL_Event& event) {
01044     if (event.key.keysym.sym == SDLK_TAB) {
01045         whiteboard_manager_->set_invert_behavior(true);
01046     }
01047 }
01048 
01049 void play_controller::process_keyup_event(const SDL_Event& event) {
01050     // If the user has pressed 1 through 9, we want to show
01051     // how far the unit can move in that many turns
01052     if(event.key.keysym.sym >= '1' && event.key.keysym.sym <= '7') {
01053         const int new_path_turns = (event.type == SDL_KEYDOWN) ?
01054                                    event.key.keysym.sym - '1' : 0;
01055 
01056         if(new_path_turns != mouse_handler_.get_path_turns()) {
01057             mouse_handler_.set_path_turns(new_path_turns);
01058 
01059             const unit_map::iterator u = mouse_handler_.selected_unit();
01060 
01061             if(u != units_.end()) {
01062                 // if it's not the unit's turn, we reset its moves
01063                 unit_movement_resetter move_reset(*u, u->side() != player_number_);
01064 
01065                 mouse_handler_.set_current_paths(pathfind::paths(map_, units_, *u,
01066                                        teams_,false,true, teams_[gui_->viewing_team()],
01067                                        mouse_handler_.get_path_turns()));
01068 
01069                 gui_->highlight_reach(mouse_handler_.current_paths());
01070             }
01071         }
01072     } else if (event.key.keysym.sym == SDLK_TAB) {
01073         static CKey keys;
01074         if (!keys[SDLK_TAB]) {
01075             whiteboard_manager_->set_invert_behavior(false);
01076         }
01077     }
01078 }
01079 
01080 void play_controller::post_mouse_press(const SDL_Event& /*event*/)
01081 {
01082     if (mouse_handler_.get_undo()){
01083         mouse_handler_.set_undo(false);
01084         menu_handler_.undo(player_number_);
01085     }
01086 }
01087 
01088 static void trim_items(std::vector<std::string>& newitems) {
01089     if (newitems.size() > 5) {
01090         std::vector<std::string> subitems;
01091         subitems.push_back(newitems[0]);
01092         subitems.push_back(newitems[1]);
01093         subitems.push_back(newitems[newitems.size() / 3]);
01094         subitems.push_back(newitems[newitems.size() * 2 / 3]);
01095         subitems.push_back(newitems.back());
01096         newitems = subitems;
01097     }
01098 }
01099 
01100 void play_controller::expand_autosaves(std::vector<std::string>& items)
01101 {
01102     savenames_.clear();
01103     for (unsigned int i = 0; i < items.size(); ++i) {
01104         if (items[i] == "AUTOSAVES") {
01105             items.erase(items.begin() + i);
01106             std::vector<std::string> newitems;
01107             std::vector<std::string> newsaves;
01108             for (unsigned int turn = this->turn(); turn != 0; turn--) {
01109                 std::string name = gamestate_.classification().label + "-" + _("Auto-Save") + lexical_cast<std::string>(turn);
01110                 if (savegame::save_game_exists(name, preferences::compress_saves())) {
01111                     if(preferences::compress_saves()) {
01112                         newsaves.push_back(name + ".gz");
01113                     } else {
01114                         newsaves.push_back(name);
01115                     }
01116                     newitems.push_back(_("Back to Turn ") + lexical_cast<std::string>(turn));
01117                 }
01118             }
01119 
01120             const std::string& start_name = gamestate_.classification().label;
01121             if(savegame::save_game_exists(start_name, preferences::compress_saves())) {
01122                 if(preferences::compress_saves()) {
01123                     newsaves.push_back(start_name + ".gz");
01124                 } else {
01125                     newsaves.push_back(start_name);
01126                 }
01127                 newitems.push_back(_("Back to Start"));
01128             }
01129 
01130             // Make sure list doesn't get too long: keep top two,
01131             // midpoint and bottom.
01132             trim_items(newitems);
01133             trim_items(newsaves);
01134 
01135             items.insert(items.begin()+i, newitems.begin(), newitems.end());
01136             savenames_.insert(savenames_.end(), newsaves.begin(), newsaves.end());
01137             break;
01138         }
01139         savenames_.push_back("");
01140     }
01141 }
01142 
01143 void play_controller::expand_wml_commands(std::vector<std::string>& items)
01144 {
01145     wml_commands_.clear();
01146     for (unsigned int i = 0; i < items.size(); ++i) {
01147         if (items[i] == "wml") {
01148             items.erase(items.begin() + i);
01149             std::map<std::string, wml_menu_item*>& gs_wmi = gamestate_.wml_menu_items;
01150             if(gs_wmi.empty())
01151                 break;
01152             std::vector<std::string> newitems;
01153 
01154             const map_location& hex = mouse_handler_.get_last_hex();
01155             gamestate_.get_variable("x1") = hex.x + 1;
01156             gamestate_.get_variable("y1") = hex.y + 1;
01157             scoped_xy_unit highlighted_unit("unit", hex.x, hex.y, units_);
01158 
01159             std::map<std::string, wml_menu_item*>::iterator itor;
01160             for (itor = gs_wmi.begin(); itor != gs_wmi.end()
01161                 && newitems.size() < MAX_WML_COMMANDS; ++itor) {
01162                 config& show_if = itor->second->show_if;
01163                 config filter_location = itor->second->filter_location;
01164                 if ((show_if.empty()
01165                     || game_events::conditional_passed(vconfig(show_if)))
01166                 && (filter_location.empty()
01167                     || terrain_filter(vconfig(filter_location), units_)(hex))
01168                 && (!itor->second->needs_select
01169                     || gamestate_.last_selected.valid()))
01170                 {
01171                     wml_commands_.push_back(itor->second);
01172                     std::string newitem = itor->second->description;
01173 
01174                     // Prevent accidental hotkey binding by appending a space
01175                     newitem.push_back(' ');
01176                     newitems.push_back(newitem);
01177                 }
01178             }
01179             items.insert(items.begin()+i, newitems.begin(), newitems.end());
01180             break;
01181         }
01182         wml_commands_.push_back(NULL);
01183     }
01184 }
01185 
01186 void play_controller::show_menu(const std::vector<std::string>& items_arg, int xloc, int yloc, bool context_menu)
01187 {
01188     std::vector<std::string> items = items_arg;
01189     hotkey::HOTKEY_COMMAND command;
01190     std::vector<std::string>::iterator i = items.begin();
01191     while(i != items.end()) {
01192         if (*i == "AUTOSAVES") {
01193             // Autosave visibility is similar to LOAD_GAME hotkey
01194             command = hotkey::HOTKEY_LOAD_GAME;
01195         } else {
01196             command = hotkey::get_hotkey(*i).get_id();
01197         }
01198         // Remove WML commands if they would not be allowed here
01199         if(*i == "wml") {
01200             if(!context_menu || gui_->viewing_team() != gui_->playing_team()
01201             || events::commands_disabled || !teams_[gui_->viewing_team()].is_human()
01202             || (linger_ && !game_config::debug)){
01203                 i = items.erase(i);
01204                 continue;
01205             }
01206         // Remove commands that can't be executed or don't belong in this type of menu
01207         } else if(!can_execute_command(command)
01208         || (context_menu && !in_context_menu(command))) {
01209             i = items.erase(i);
01210             continue;
01211         }
01212         ++i;
01213     }
01214 
01215     // Add special non-hotkey items to the menu and remember their indices
01216     expand_autosaves(items);
01217     expand_wml_commands(items);
01218 
01219     if(items.empty())
01220         return;
01221 
01222     command_executor::show_menu(items, xloc, yloc, context_menu, *gui_);
01223 }
01224 
01225 bool play_controller::in_context_menu(hotkey::HOTKEY_COMMAND command) const
01226 {
01227     switch(command) {
01228     // Only display these if the mouse is over a castle or keep tile
01229     case hotkey::HOTKEY_RECRUIT:
01230     case hotkey::HOTKEY_REPEAT_RECRUIT:
01231     case hotkey::HOTKEY_RECALL: {
01232         wb::future_map future; //< lasts until method returns.
01233         // last_hex_ is set by mouse_events::mouse_motion
01234         // Enable recruit/recall on castle/keep tiles
01235         for(unit_map::const_iterator leader = units_.begin();
01236                 leader != units_.end();++leader) {
01237             if (leader->can_recruit() &&
01238                 leader->side() == resources::screen->viewing_side() &&
01239                 can_recruit_on(map_, leader->get_location(), mouse_handler_.get_last_hex()))
01240                 return true;
01241         }
01242         return false;
01243     }
01244     default:
01245         return true;
01246     }
01247 }
01248 
01249 std::string play_controller::get_action_image(hotkey::HOTKEY_COMMAND command, int index) const
01250 {
01251     if(index >= 0 && index < static_cast<int>(wml_commands_.size())) {
01252         wml_menu_item* const& wmi = wml_commands_[index];
01253         if(wmi != NULL) {
01254             return wmi->image.empty() ? game_config::images::wml_menu : wmi->image;
01255         }
01256     }
01257     return command_executor::get_action_image(command, index);
01258 }
01259 
01260 hotkey::ACTION_STATE play_controller::get_action_state(hotkey::HOTKEY_COMMAND command, int /*index*/) const
01261 {
01262     switch(command) {
01263     case hotkey::HOTKEY_DELAY_SHROUD:
01264         return teams_[gui_->viewing_team()].auto_shroud_updates() ? hotkey::ACTION_OFF : hotkey::ACTION_ON;
01265     default:
01266         return hotkey::ACTION_STATELESS;
01267     }
01268 }
01269 
01270 namespace {
01271     static const std::string empty_str = "";
01272 }
01273 
01274 const std::string& play_controller::select_victory_music() const
01275 {
01276     if(victory_music_.empty())
01277         return empty_str;
01278     return victory_music_[rand() % victory_music_.size()];
01279 }
01280 
01281 const std::string& play_controller::select_defeat_music() const
01282 {
01283     if(defeat_music_.empty())
01284         return empty_str;
01285     return defeat_music_[rand() % defeat_music_.size()];
01286 }
01287 
01288 
01289 void play_controller::set_victory_music_list(const std::string& list)
01290 {
01291     victory_music_ = utils::split(list);
01292     if(victory_music_.empty())
01293         victory_music_ = utils::split(game_config::default_victory_music);
01294 }
01295 
01296 void play_controller::set_defeat_music_list(const std::string& list)
01297 {
01298     defeat_music_  = utils::split(list);
01299     if(defeat_music_.empty())
01300         defeat_music_ = utils::split(game_config::default_defeat_music);
01301 }
01302 
01303 void play_controller::check_victory()
01304 {
01305     check_end_level();
01306 
01307     std::vector<unsigned> seen_leaders;
01308     for (unit_map::const_iterator i = units_.begin(),
01309          i_end = units_.end(); i != i_end; ++i)
01310     {
01311         if (i->can_recruit()) {
01312             DBG_NG << "seen leader for side " << i->side() << "\n";
01313             seen_leaders.push_back(i->side());
01314         }
01315     }
01316 
01317     // Clear villages for teams that have no leader
01318     for (std::vector<team>::iterator tm_beg = teams_.begin(), tm = tm_beg,
01319          tm_end = teams_.end(); tm != tm_end; ++tm)
01320     {
01321         if (std::find(seen_leaders.begin(), seen_leaders.end(), tm - tm_beg + 1) == seen_leaders.end()) {
01322             tm->clear_villages();
01323             // invalidate_all() is overkill and expensive but this code is
01324             // run rarely so do it the expensive way.
01325             gui_->invalidate_all();
01326         }
01327     }
01328 
01329     bool found_player = false;
01330 
01331     for (size_t n = 0; n != seen_leaders.size(); ++n) {
01332         size_t side = seen_leaders[n] - 1;
01333 
01334         for (size_t m = n +1 ; m != seen_leaders.size(); ++m) {
01335             if (teams_[side].is_enemy(seen_leaders[m])) {
01336                 return;
01337             }
01338         }
01339 
01340         if (teams_[side].is_human()) {
01341             found_player = true;
01342         }
01343     }
01344 
01345     if (found_player) {
01346         game_events::fire("enemies defeated");
01347         check_end_level();
01348     }
01349 
01350     if (!victory_when_enemies_defeated_ && (found_player || is_observer())) {
01351         // This level has asked not to be ended by this condition.
01352         return;
01353     }
01354 
01355     if (non_interactive()) {
01356         std::cout << "winner: ";
01357         foreach (unsigned l, seen_leaders) {
01358             std::string ai = ai::manager::get_active_ai_identifier_for_side(l);
01359             if (ai.empty()) ai = "default ai";
01360             std::cout << l << " (using " << ai << ") ";
01361         }
01362         std::cout << '\n';
01363         ai_testing::log_victory(seen_leaders);
01364     }
01365 
01366     DBG_NG << "throwing end level exception...\n";
01367     throw end_level_exception(found_player ? VICTORY : DEFEAT);
01368 }
01369 
01370 void play_controller::process_oos(const std::string& msg) const
01371 {
01372     if (game_config::ignore_replay_errors) return;
01373 
01374     std::stringstream message;
01375     message << _("The game is out of sync. It might not make much sense to continue. Do you want to save your game?");
01376     message << "\n\n" << _("Error details:") << "\n\n" << msg;
01377 
01378     savegame::oos_savegame save(to_config());
01379     save.save_game_interactive(resources::screen->video(), message.str(), gui::YES_NO); // can throw end_level_exception
01380 }
01381 
01382 void play_controller::update_gui_to_player(const int team_index, const bool observe)
01383 {
01384     gui_->set_team(team_index, observe);
01385     gui_->recalculate_minimap();
01386     gui_->invalidate_all();
01387     gui_->draw(true,true);
01388 }
01389 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines

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