playsingle_controller.cpp

Go to the documentation of this file.
00001 /* $Id: playsingle_controller.cpp 54192 2012-05-17 20:07:06Z anonymissimus $ */
00002 
00003 /*
00004    Copyright (C) 2006 - 2012 by Joerg Hinrichs <joerg.hinrichs@alice-dsl.de>
00005    wesnoth playlevel Copyright (C) 2003 by David White <dave@whitevine.net>
00006    Part of the Battle for Wesnoth Project http://www.wesnoth.org/
00007 
00008    This program is free software; you can redistribute it and/or modify
00009    it under the terms of the GNU General Public License as published by
00010    the Free Software Foundation; either version 2 of the License, or
00011    (at your option) any later version.
00012    This program is distributed in the hope that it will be useful,
00013    but WITHOUT ANY WARRANTY.
00014 
00015    See the COPYING file for more details.
00016 */
00017 
00018 /**
00019  *  @file
00020  *  Logic for single-player game.
00021  */
00022 
00023 #include "playsingle_controller.hpp"
00024 
00025 #include "actions.hpp"
00026 #include "ai/manager.hpp"
00027 #include "ai/game_info.hpp"
00028 #include "ai/testing.hpp"
00029 #include "dialogs.hpp"
00030 #include "foreach.hpp"
00031 #include "game_end_exceptions.hpp"
00032 #include "game_events.hpp"
00033 #include "game_preferences.hpp"
00034 #include "gettext.hpp"
00035 #include "gui/dialogs/transient_message.hpp"
00036 #include "log.hpp"
00037 #include "map_label.hpp"
00038 #include "marked-up_text.hpp"
00039 #include "playturn.hpp"
00040 #include "resources.hpp"
00041 #include "savegame.hpp"
00042 #include "sound.hpp"
00043 #include "formula_string_utils.hpp"
00044 #include "events.hpp"
00045 #include "save_blocker.hpp"
00046 #include "soundsource.hpp"
00047 #include "storyscreen/interface.hpp"
00048 #include "whiteboard/manager.hpp"
00049 #include "util.hpp"
00050 
00051 static lg::log_domain log_engine("engine");
00052 #define ERR_NG LOG_STREAM(err, log_engine)
00053 #define LOG_NG LOG_STREAM(info, log_engine)
00054 
00055 playsingle_controller::playsingle_controller(const config& level,
00056         game_state& state_of_game, const int ticks, const int num_turns,
00057         const config& game_config, CVideo& video, bool skip_replay) :
00058     play_controller(level, state_of_game, ticks, num_turns, game_config, video, skip_replay),
00059     cursor_setter(cursor::NORMAL),
00060     data_backlog_(),
00061     textbox_info_(),
00062     replay_sender_(recorder),
00063     end_turn_(false),
00064     player_type_changed_(false),
00065     replaying_(false),
00066     turn_over_(false),
00067     skip_next_turn_(false),
00068     level_result_(NONE)
00069 {
00070     // game may need to start in linger mode
00071     if (state_of_game.classification().completion == "victory" || state_of_game.classification().completion == "defeat")
00072     {
00073         LOG_NG << "Setting linger mode.\n";
00074         browse_ = linger_ = true;
00075     }
00076 
00077     ai::game_info ai_info;
00078     ai::manager::set_ai_info(ai_info);
00079     ai::manager::add_observer(this) ;
00080 }
00081 
00082 playsingle_controller::~playsingle_controller()
00083 {
00084     ai::manager::remove_observer(this) ;
00085     ai::manager::clear_ais() ;
00086 }
00087 
00088 void playsingle_controller::init_gui(){
00089     LOG_NG << "Initializing GUI... " << (SDL_GetTicks() - ticks_) << "\n";
00090     play_controller::init_gui();
00091 
00092     if(first_human_team_ != -1) {
00093         gui_->scroll_to_tile(map_.starting_position(first_human_team_ + 1), game_display::WARP);
00094     }
00095     gui_->scroll_to_tile(map_.starting_position(1), game_display::WARP);
00096 
00097     update_locker lock_display(gui_->video(),recorder.is_skipping());
00098     events::raise_draw_event();
00099     gui_->draw();
00100 }
00101 
00102 void playsingle_controller::recruit(){
00103     if (!browse_)
00104         menu_handler_.recruit(player_number_, mouse_handler_.get_last_hex());
00105     else if (resources::whiteboard->is_active())
00106         menu_handler_.recruit(resources::screen->viewing_side(), mouse_handler_.get_last_hex());
00107 }
00108 
00109 void playsingle_controller::repeat_recruit(){
00110     if (!browse_)
00111         menu_handler_.repeat_recruit(player_number_, mouse_handler_.get_last_hex());
00112     else if (resources::whiteboard->is_active())
00113         menu_handler_.repeat_recruit(resources::screen->viewing_side(), mouse_handler_.get_last_hex());
00114 }
00115 
00116 void playsingle_controller::recall(){
00117     if (!browse_)
00118         menu_handler_.recall(player_number_, mouse_handler_.get_last_hex());
00119     else if (resources::whiteboard->is_active())
00120         menu_handler_.recall(resources::screen->viewing_side(), mouse_handler_.get_last_hex());
00121 }
00122 
00123 void playsingle_controller::toggle_shroud_updates(){
00124     menu_handler_.toggle_shroud_updates(gui_->viewing_team()+1);
00125 }
00126 
00127 void playsingle_controller::update_shroud_now(){
00128     menu_handler_.update_shroud_now(gui_->viewing_team()+1);
00129 }
00130 
00131 void playsingle_controller::end_turn(){
00132     if (linger_)
00133         end_turn_ = true;
00134     else if (!browse_){
00135         browse_ = true;
00136         end_turn_ = menu_handler_.end_turn(player_number_);
00137         browse_ = end_turn_;
00138     }
00139 }
00140 
00141 void playsingle_controller::force_end_turn(){
00142     skip_next_turn_ = true;
00143     end_turn_ = true;
00144 }
00145 
00146 void playsingle_controller::check_end_level()
00147 {
00148     if (level_result_ == NONE || linger_)
00149     {
00150         team &t = teams_[gui_->viewing_team()];
00151         if (!browse_ && t.objectives_changed()) {
00152             dialogs::show_objectives(level_, t.objectives());
00153             t.reset_objectives_changed();
00154         }
00155         return;
00156     }
00157     throw end_level_exception(level_result_);
00158 }
00159 
00160 void playsingle_controller::rename_unit(){
00161     menu_handler_.rename_unit();
00162 }
00163 
00164 void playsingle_controller::create_unit(){
00165     menu_handler_.create_unit(mouse_handler_);
00166 }
00167 
00168 void playsingle_controller::change_side(){
00169     menu_handler_.change_side(mouse_handler_);
00170 }
00171 
00172 void playsingle_controller::label_terrain(bool team_only){
00173     menu_handler_.label_terrain(mouse_handler_, team_only);
00174 }
00175 
00176 void playsingle_controller::clear_labels(){
00177     menu_handler_.clear_labels();
00178 }
00179 
00180 void playsingle_controller::continue_move(){
00181     menu_handler_.continue_move(mouse_handler_, player_number_);
00182 }
00183 
00184 void playsingle_controller::unit_hold_position(){
00185     if (!browse_)
00186         menu_handler_.unit_hold_position(mouse_handler_, player_number_);
00187 }
00188 
00189 void playsingle_controller::end_unit_turn(){
00190     if (!browse_)
00191         menu_handler_.end_unit_turn(mouse_handler_, player_number_);
00192 }
00193 
00194 void playsingle_controller::user_command(){
00195     menu_handler_.user_command();
00196 }
00197 
00198 void playsingle_controller::custom_command(){
00199     menu_handler_.custom_command();
00200 }
00201 
00202 void playsingle_controller::ai_formula(){
00203     menu_handler_.ai_formula();
00204 }
00205 
00206 void playsingle_controller::clear_messages(){
00207     menu_handler_.clear_messages();
00208 }
00209 
00210 void playsingle_controller::whiteboard_toggle() {
00211     resources::whiteboard->set_active(!resources::whiteboard->is_active());
00212     //@todo Stop printing whiteboard help in the chat once we have better documentation/help
00213     resources::whiteboard->print_help_once();
00214 }
00215 
00216 void playsingle_controller::whiteboard_execute_action(){
00217     whiteboard_manager_->contextual_execute();
00218 }
00219 
00220 void playsingle_controller::whiteboard_execute_all_actions(){
00221     whiteboard_manager_->execute_all_actions();
00222 }
00223 
00224 void playsingle_controller::whiteboard_delete_action(){
00225     whiteboard_manager_->contextual_delete();
00226 }
00227 
00228 void playsingle_controller::whiteboard_bump_up_action()
00229 {
00230     whiteboard_manager_->contextual_bump_up_action();
00231 }
00232 
00233 void playsingle_controller::whiteboard_bump_down_action()
00234 {
00235     whiteboard_manager_->contextual_bump_down_action();
00236 }
00237 
00238 void playsingle_controller::whiteboard_suppose_dead()
00239 {
00240     unit* curr_unit;
00241     map_location loc;
00242     { wb::future_map future; //start planned unit map scope
00243         curr_unit = &*menu_handler_.current_unit();
00244         loc = curr_unit->get_location();
00245     } // end planned unit map scope
00246     whiteboard_manager_->save_suppose_dead(*curr_unit,loc);
00247 }
00248 
00249 void playsingle_controller::report_victory(
00250     std::ostringstream &report, int player_gold, int remaining_gold,
00251     int finishing_bonus_per_turn, int turns_left, int finishing_bonus)
00252 {
00253     const end_level_data &end_level = get_end_level_data_const();
00254     report << _("Remaining gold: ")
00255            << utils::half_signed_value(remaining_gold) << "\n";
00256     if(end_level.gold_bonus) {
00257         if (turns_left > -1) {
00258             report << _("Early finish bonus: ")
00259                    << finishing_bonus_per_turn
00260                    << " " << _("per turn") << "\n"
00261                    << "<b>" << _("Turns finished early: ")
00262                    << turns_left << "</b>\n"
00263                    << _("Bonus: ")
00264                    << finishing_bonus << "\n";
00265         }
00266         report << _("Gold: ")
00267                << utils::half_signed_value(remaining_gold + finishing_bonus);
00268     }
00269     if (remaining_gold > 0) {
00270         report << '\n' << _("Carry over percentage: ") << end_level.carryover_percentage;
00271     }
00272     if(end_level.carryover_add) {
00273         report << "\n<b>" << _("Bonus Gold: ") << utils::half_signed_value(player_gold) <<"</b>";
00274     } else {
00275         report << "\n<b>" << _("Retained Gold: ") << utils::half_signed_value(player_gold) << "</b>";
00276     }
00277 
00278     std::string goldmsg;
00279     utils::string_map symbols;
00280 
00281     symbols["gold"] = lexical_cast_default<std::string>(player_gold);
00282 
00283     // Note that both strings are the same in english, but some languages will
00284     // want to translate them differently.
00285     if(end_level.carryover_add) {
00286         if(player_gold > 0) {
00287             goldmsg = vngettext(
00288                     "You will start the next scenario with $gold "
00289                     "on top of the defined minimum starting gold.",
00290                     "You will start the next scenario with $gold "
00291                     "on top of the defined minimum starting gold.",
00292                     player_gold, symbols);
00293 
00294         } else {
00295             goldmsg = vngettext(
00296                     "You will start the next scenario with "
00297                     "the defined minimum starting gold.",
00298                     "You will start the next scenario with "
00299                     "the defined minimum starting gold.",
00300                     player_gold, symbols);
00301         }
00302     } else {
00303         goldmsg = vngettext(
00304             "You will start the next scenario with $gold "
00305             "or its defined minimum starting gold, "
00306             "whichever is higher.",
00307             "You will start the next scenario with $gold "
00308             "or its defined minimum starting gold, "
00309             "whichever is higher.",
00310             player_gold, symbols);
00311     }
00312 
00313     // xgettext:no-c-format
00314     report << '\n' << goldmsg;
00315 }
00316 
00317 LEVEL_RESULT playsingle_controller::play_scenario(
00318     const config::const_child_itors &story,
00319     bool skip_replay)
00320 {
00321     LOG_NG << "in playsingle_controller::play_scenario()...\n";
00322 
00323     // Start music.
00324     foreach (const config &m, level_.child_range("music")) {
00325         sound::play_music_config(m);
00326     }
00327     sound::commit_music_changes();
00328 
00329     if(!skip_replay) {
00330         show_story(*gui_, level_["name"], story);
00331     }
00332     gui_->labels().read(level_);
00333 
00334     // Read sound sources
00335     assert(soundsources_manager_ != NULL);
00336     foreach (const config &s, level_.child_range("sound_source")) {
00337         soundsource::sourcespec spec(s);
00338         soundsources_manager_->add(spec);
00339     }
00340 
00341     set_victory_when_enemies_defeated(level_["victory_when_enemies_defeated"].to_bool(true));
00342     end_level_data &end_level = get_end_level_data();
00343     end_level.carryover_percentage = level_["carryover_percentage"].to_int(game_config::gold_carryover_percentage);
00344     end_level.carryover_add = level_["carryover_add"].to_bool();
00345 
00346     bool past_prestart = false;
00347 
00348     LOG_NG << "entering try... " << (SDL_GetTicks() - ticks_) << "\n";
00349     try {
00350 
00351         fire_prestart(!loading_game_);
00352         init_gui();
00353 
00354         past_prestart = true;
00355 
00356         LOG_NG << "first_time..." << (recorder.is_skipping() ? "skipping" : "no skip") << "\n";
00357 
00358         events::raise_draw_event();
00359         fire_start(!loading_game_);
00360         gui_->recalculate_minimap();
00361 
00362         replaying_ = (recorder.at_end() == false);
00363 
00364         LOG_NG << "starting main loop\n" << (SDL_GetTicks() - ticks_) << "\n";
00365 
00366         // Initialize countdown clock.
00367         std::vector<team>::iterator t;
00368         for(t = teams_.begin(); t != teams_.end(); ++t) {
00369             if (gamestate_.mp_settings().mp_countdown && !loading_game_ ){
00370                 t->set_countdown_time(1000 * gamestate_.mp_settings().mp_countdown_init_time);
00371             }
00372         }
00373 
00374         // if we loaded a save file in linger mode, skip to it.
00375         if (linger_) {
00376             //determine the bonus gold handling for this scenario
00377             end_level.read(level_.child_or_empty("endlevel"));
00378             end_level.carryover_report = false;
00379             end_level.disabled = true;
00380             throw end_level_exception(SKIP_TO_LINGER);
00381         }
00382 
00383         // Avoid autosaving after loading, but still
00384         // allow the first turn to have an autosave.
00385         bool save = !loading_game_;
00386         ai_testing::log_game_start();
00387         for(; ; first_player_ = 1) {
00388             play_turn(save);
00389             save = true;
00390         } //end for loop
00391 
00392 #ifdef _MSC_VER
00393 //MSVC claims that const game::load_game_exception& lge would be unreferenced...
00394 #pragma warning (push)
00395 #pragma warning (disable : 4101)
00396 #endif
00397     } catch(const game::load_game_exception& lge) {
00398         // Loading a new game is effectively a quit.
00399         //
00400         if (lge.game != "") {
00401             gamestate_ = game_state();
00402         }
00403         throw;
00404     } catch (end_level_exception &end_level_exn) {
00405         if(!past_prestart) {
00406             draw_solid_tinted_rectangle(
00407                 0, 0, gui_->video().getx(), gui_->video().gety(), 0, 0, 0, 1.0,
00408                 gui_->video().getSurface()
00409             );
00410             update_rect(0, 0, gui_->video().getx(), gui_->video().gety());
00411         }
00412 
00413         ai_testing::log_game_end();
00414         LEVEL_RESULT end_level_result = end_level_exn.result;
00415         if (!end_level.custom_endlevel_music.empty()) {
00416             if (end_level_result == DEFEAT) {
00417                 set_defeat_music_list(end_level.custom_endlevel_music);
00418             } else {
00419                 set_victory_music_list(end_level.custom_endlevel_music);
00420             }
00421         }
00422 
00423         if (teams_.empty())
00424         {
00425             //store persistent teams
00426             gamestate_.snapshot = config();
00427             store_recalls();
00428 
00429             return VICTORY; // this is probably only a story scenario, i.e. has its endlevel in the prestart event
00430         }
00431         const bool obs = is_observer();
00432         if (game_config::exit_at_end) {
00433             exit(0);
00434         }
00435         if (end_level_result == DEFEAT || end_level_result == VICTORY)
00436         {
00437             gamestate_.classification().completion = (end_level_exn.result == VICTORY) ? "victory" : "defeat";
00438             // If we're a player, and the result is victory/defeat, then send
00439             // a message to notify the server of the reason for the game ending.
00440             if (!obs) {
00441                 config cfg;
00442                 config& info = cfg.add_child("info");
00443                 info["type"] = "termination";
00444                 info["condition"] = "game over";
00445                 info["result"] = gamestate_.classification().completion;
00446                 network::send_data(cfg, 0);
00447             } else {
00448                 gui2::show_transient_message(gui_->video(),_("Game Over"),
00449                                     _("The game is over."));
00450                 return OBSERVER_END;
00451             }
00452         }
00453 
00454         if (end_level_result == QUIT) {
00455             return QUIT;
00456         }
00457         else if (end_level_result == DEFEAT)
00458         {
00459             gamestate_.classification().completion = "defeat";
00460             game_events::fire("defeat");
00461 
00462             if (!obs) {
00463                 const std::string& defeat_music = select_defeat_music();
00464                 if(defeat_music.empty() != true)
00465                     sound::play_music_once(defeat_music);
00466 
00467                 return DEFEAT;
00468             } else {
00469                 return QUIT;
00470             }
00471         }
00472         else if (end_level_result == VICTORY)
00473         {
00474             gamestate_.classification().completion =
00475                 !end_level.linger_mode ? "running" : "victory";
00476             game_events::fire("victory");
00477 
00478             //
00479             // Play victory music once all victory events
00480             // are finished, if we aren't observers.
00481             //
00482             // Some scenario authors may use 'continue'
00483             // result for something that is not story-wise
00484             // a victory, so let them use [music] tags
00485             // instead should they want special music.
00486             //
00487             if (!obs && end_level.linger_mode) {
00488                 const std::string& victory_music = select_victory_music();
00489                 if(victory_music.empty() != true)
00490                     sound::play_music_once(victory_music);
00491             }
00492 
00493             // Add all the units that survived the scenario.
00494             LOG_NG << "Add units that survived the scenario to the recall list.\n";
00495             for(unit_map::iterator un = units_.begin(); un != units_.end(); ++un) {
00496 
00497                 if (teams_[un->side() - 1].persistent()) {
00498                     LOG_NG << "Added unit " << un->id() << ", " << un->name() << "\n";
00499                     un->new_turn();
00500                     un->new_scenario();
00501                     teams_[un->side() - 1].recall_list().push_back(*un);
00502                 }
00503             }
00504             //store all units that survived (recall list for the next scenario) in snapshot
00505             gamestate_.snapshot = config();
00506             store_recalls();
00507             //store gold and report victory
00508             store_gold(obs);
00509             return VICTORY;
00510         }
00511         else if (end_level_result == SKIP_TO_LINGER)
00512         {
00513             LOG_NG << "resuming from loaded linger state...\n";
00514             //as carryover information is stored in the snapshot, we have to re-store it after loading a linger state
00515             gamestate_.snapshot = config();
00516             store_recalls();
00517             store_gold();
00518             return VICTORY;
00519         }
00520     } // end catch
00521     catch(network::error& e) {
00522         bool disconnect = false;
00523         if(e.socket) {
00524             e.disconnect();
00525             disconnect = true;
00526         }
00527 
00528         savegame::game_savegame save(gamestate_, *gui_, to_config(), preferences::compress_saves());
00529         save.save_game_interactive(gui_->video(), _("A network disconnection has occurred, and the game cannot continue. Do you want to save the game?"), gui::YES_NO);
00530         if(disconnect) {
00531             throw network::error();
00532         } else {
00533             return QUIT;
00534         }
00535     }
00536 
00537     return QUIT;
00538 }
00539 #ifdef _MSC_VER
00540 #pragma warning (pop)
00541 #endif
00542 
00543 void playsingle_controller::play_turn(bool save)
00544 {
00545     resources::whiteboard->on_gamestate_change();
00546     gui_->new_turn();
00547     gui_->invalidate_game_status();
00548     events::raise_draw_event();
00549 
00550     LOG_NG << "turn: " << turn() << "\n";
00551 
00552     if(non_interactive())
00553         std::cout << "Turn " << turn() << ":" << std::endl;
00554 
00555 
00556     for (player_number_ = first_player_; player_number_ <= int(teams_.size()); ++player_number_)
00557     {
00558         // If a side is empty skip over it.
00559         if (current_team().is_empty()) continue;
00560         try {
00561             save_blocker blocker;
00562             init_side(player_number_ - 1);
00563         } catch (end_turn_exception) {
00564             if (current_team().is_network() == false) {
00565                 turn_info turn_data(player_number_, replay_sender_);
00566                 recorder.end_turn();
00567                 turn_data.sync_network();
00568             }
00569             continue;
00570         }
00571 
00572         if (replaying_) {
00573             LOG_NG << "doing replay " << player_number_ << "\n";
00574             replaying_ = ::do_replay(player_number_);
00575             LOG_NG << "result of replay: " << (replaying_?"true":"false") << "\n";
00576         } else {
00577             // If a side is dead end the turn, but play at least side=1's
00578             // turn in case all sides are dead
00579             if (current_team().is_human() && side_units(player_number_) == 0
00580                 && (resources::units->size() != 0 || player_number_ != 1))
00581             {
00582                 turn_info turn_data(player_number_, replay_sender_);
00583                 recorder.end_turn();
00584                 turn_data.sync_network();
00585                 continue;
00586             }
00587             ai_testing::log_turn_start(player_number_);
00588             play_side(player_number_, save);
00589         }
00590 
00591         finish_side_turn();
00592 
00593         if(non_interactive()) {
00594             std::cout << " Player " << player_number_ << ": " <<
00595                 current_team().villages().size() << " Villages" <<
00596                 std::endl;
00597             ai_testing::log_turn_end(player_number_);
00598         }
00599 
00600         check_victory();
00601 
00602         //if loading a savegame, network turns might not have reset this yet
00603         loading_game_ = false;
00604     }
00605     //If the loop exits due to the last team having been processed,
00606     //player_number_ will be 1 too high
00607     if(player_number_ > static_cast<int>(teams_.size()))
00608         player_number_ = teams_.size();
00609 
00610     finish_turn();
00611 
00612     // Time has run out
00613     check_time_over();
00614 }
00615 
00616 void playsingle_controller::play_side(const unsigned int side_number, bool save)
00617 {
00618     //check for team-specific items in the scenario
00619     gui_->parse_team_overlays();
00620 
00621     //flag used when we fallback from ai and give temporarily control to human
00622     bool temporary_human = false;
00623     do {
00624         // Although this flag is used only in this method,
00625         // it has to be a class member since derived classes
00626         // rely on it
00627         player_type_changed_ = false;
00628         if (!skip_next_turn_)
00629             end_turn_ = false;
00630 
00631 
00632         statistics::reset_turn_stats(teams_[side_number - 1].save_id());
00633 
00634         if(current_team().is_human() || temporary_human) {
00635             LOG_NG << "is human...\n";
00636             try{
00637                 before_human_turn(save);
00638                 play_human_turn();
00639                 after_human_turn();
00640             } catch(end_turn_exception& end_turn) {
00641                 if (end_turn.redo == side_number) {
00642                     player_type_changed_ = true;
00643                     // If new controller is not human,
00644                     // reset gui to prev human one
00645                     if (!teams_[side_number-1].is_human()) {
00646                         browse_ = true;
00647                         int t = find_human_team_before(side_number);
00648                         if (t > 0) {
00649                             update_gui_to_player(t-1);
00650                         }
00651                     }
00652                 }
00653             }
00654 
00655             LOG_NG << "human finished turn...\n";
00656         } else if(current_team().is_ai()) {
00657             try {
00658                 play_ai_turn();
00659             } catch(fallback_ai_to_human_exception&) {
00660                 //give control to human for the rest of this turn
00661                 player_type_changed_ = true;
00662                 temporary_human = true;
00663             }
00664         }
00665     } while (player_type_changed_);
00666     // Keep looping if the type of a team (human/ai/networked)
00667     // has changed mid-turn
00668     skip_next_turn_ = false;
00669 }
00670 
00671 void playsingle_controller::before_human_turn(bool save)
00672 {
00673     log_scope("player turn");
00674     browse_ = false;
00675     linger_ = false;
00676 
00677 
00678     ai::manager::raise_turn_started();
00679 
00680     if(save && level_result_ == NONE) {
00681         savegame::autosave_savegame save(gamestate_, *gui_, to_config(), preferences::compress_saves());
00682         save.autosave(game_config::disable_autosave, preferences::autosavemax(), preferences::INFINITE_AUTO_SAVES);
00683     }
00684 
00685     if(preferences::turn_bell() && level_result_ == NONE) {
00686         sound::play_bell(game_config::sounds::turn_bell);
00687     }
00688 }
00689 
00690 void playsingle_controller::show_turn_dialog(){
00691     if(preferences::turn_dialog() && (level_result_ == NONE) ) {
00692         std::string message = _("It is now $name|’s turn");
00693         utils::string_map symbols;
00694         symbols["name"] = teams_[player_number_ - 1].current_player();
00695         message = utils::interpolate_variables_into_string(message, &symbols);
00696         gui2::show_transient_message(gui_->video(), "", message);
00697     }
00698 }
00699 
00700 void playsingle_controller::execute_gotos(){
00701     menu_handler_.execute_gotos(mouse_handler_, player_number_);
00702 }
00703 
00704 void playsingle_controller::play_human_turn() {
00705     show_turn_dialog();
00706     execute_gotos();
00707 
00708     gui_->enable_menu("endturn", true);
00709     while(!end_turn_) {
00710         play_slice();
00711         check_end_level();
00712         gui_->draw();
00713     }
00714 }
00715 struct set_completion
00716 {
00717     set_completion(game_state& state, const std::string& completion) :
00718         state_(state), completion_(completion)
00719     {
00720     }
00721     ~set_completion()
00722     {
00723         state_.classification().completion = completion_;
00724     }
00725     private:
00726     game_state& state_;
00727     const std::string completion_;
00728 };
00729 
00730 void playsingle_controller::linger()
00731 {
00732     LOG_NG << "beginning end-of-scenario linger\n";
00733     browse_ = true;
00734     linger_ = true;
00735 
00736     // If we need to set the status depending on the completion state
00737     // the key to it is here.
00738     gui_->set_game_mode(game_display::LINGER_SP);
00739 
00740     // this is actually for after linger mode is over -- we don't
00741     // want to stay stuck in linger state when the *next* scenario
00742     // is over.
00743     set_completion setter(gamestate_,"running");
00744 
00745     // change the end-turn button text to its alternate label
00746     gui_->get_theme().refresh_title2("button-endturn", "title2");
00747     gui_->invalidate_theme();
00748     gui_->redraw_everything();
00749 
00750     // End all unit moves
00751     for (unit_map::iterator u = units_.begin(); u != units_.end(); ++u) {
00752         u->set_user_end_turn(true);
00753     }
00754     try {
00755         // Same logic as single-player human turn, but
00756         // *not* the same as multiplayer human turn.
00757         gui_->enable_menu("endturn", true);
00758         end_turn_ = false;
00759         while(!end_turn_) {
00760             // Reset the team number to make sure we're the right team.
00761             player_number_ = first_player_;
00762             play_slice();
00763             gui_->draw();
00764         }
00765 #ifdef _MSC_VER
00766 #pragma warning (push)
00767 #pragma warning (disable : 4101)
00768 #endif
00769     } catch(const game::load_game_exception& lge) {
00770         // Loading a new game is effectively a quit.
00771         if (lge.game != "") {
00772             gamestate_ = game_state();
00773         }
00774         throw;
00775     }
00776 
00777     // revert the end-turn button text to its normal label
00778     gui_->get_theme().refresh_title2("button-endturn", "title");
00779     gui_->invalidate_theme();
00780     gui_->redraw_everything();
00781     gui_->set_game_mode(game_display::RUNNING);
00782 
00783     LOG_NG << "ending end-of-scenario linger\n";
00784 }
00785 #ifdef _MSC_VER
00786 #pragma warning (pop)
00787 #endif
00788 
00789 void playsingle_controller::end_turn_record()
00790 {
00791     if (!turn_over_)
00792     {
00793         turn_over_ = true;
00794         recorder.end_turn();
00795     }
00796 }
00797 void playsingle_controller::end_turn_record_unlock()
00798 {
00799     turn_over_ = false;
00800 }
00801 
00802 hotkey::ACTION_STATE playsingle_controller::get_action_state(hotkey::HOTKEY_COMMAND command, int index) const
00803 {
00804     switch(command) {
00805     case hotkey::HOTKEY_WB_TOGGLE:
00806         return resources::whiteboard->is_active() ? hotkey::ACTION_ON : hotkey::ACTION_OFF;
00807     default:
00808         return play_controller::get_action_state(command, index);
00809     }
00810 }
00811 
00812 
00813 
00814 void playsingle_controller::after_human_turn(){
00815     browse_ = true;
00816     end_turn_record();
00817     end_turn_record_unlock();
00818     menu_handler_.clear_undo_stack(player_number_);
00819 
00820     if(teams_[player_number_-1].uses_fog()) {
00821         // needed because currently fog is only recalculated when a hex is /un/covered
00822         recalculate_fog(player_number_);
00823     }
00824 
00825     gui_->set_route(NULL);
00826     gui_->unhighlight_reach();
00827 }
00828 
00829 void playsingle_controller::play_ai_turn(){
00830     LOG_NG << "is ai...\n";
00831     gui_->enable_menu("endturn", false);
00832     browse_ = true;
00833     gui_->recalculate_minimap();
00834 
00835     const cursor::setter cursor_setter(cursor::WAIT);
00836 
00837     turn_info turn_data(player_number_, replay_sender_);
00838 
00839     try {
00840         ai::manager::play_turn(player_number_);
00841     } catch (end_turn_exception&) {
00842     }
00843     recorder.end_turn();
00844     turn_data.sync_network();
00845 
00846     gui_->recalculate_minimap();
00847     recalculate_fog(player_number_);
00848     gui_->invalidate_unit();
00849     gui_->invalidate_game_status();
00850     gui_->invalidate_all();
00851     gui_->draw();
00852     gui_->delay(100);
00853 }
00854 
00855 void playsingle_controller::handle_generic_event(const std::string& name){
00856     if (name == "ai_user_interact"){
00857         play_slice(false);
00858     }
00859     if (end_turn_){
00860         throw end_turn_exception();
00861     }
00862 }
00863 
00864 void playsingle_controller::check_time_over(){
00865     bool b = tod_manager_.next_turn();
00866     it_is_a_new_turn_ = true;
00867     if(!b) {
00868 
00869         LOG_NG << "firing time over event...\n";
00870         game_events::fire("time over");
00871         LOG_NG << "done firing time over event...\n";
00872         //if turns are added while handling 'time over' event
00873         if (tod_manager_.is_time_left()) {
00874             return;
00875         }
00876 
00877         if(non_interactive()) {
00878             std::cout << "time over (draw)\n";
00879             ai_testing::log_draw();
00880         }
00881 
00882         check_end_level();
00883         throw end_level_exception(DEFEAT);
00884     }
00885 }
00886 
00887 void playsingle_controller::store_recalls() {
00888     std::set<std::string> side_ids;
00889     std::vector<team>::iterator i;
00890     for(i=teams_.begin(); i!=teams_.end(); ++i) {
00891         side_ids.insert(i->save_id());
00892         if (i->persistent()) {
00893             config& new_side = gamestate_.snapshot.add_child("side");
00894             new_side["save_id"] = i->save_id();
00895             new_side["name"] = i->current_player();
00896             std::stringstream can_recruit;
00897             std::copy(i->recruits().begin(),i->recruits().end(),std::ostream_iterator<std::string>(can_recruit,","));
00898             std::string can_recruit_str = can_recruit.str();
00899             // Remove the trailing comma
00900             if(can_recruit_str.empty() == false) {
00901                 can_recruit_str.resize(can_recruit_str.size()-1);
00902             }
00903             new_side["previous_recruits"] = can_recruit_str;
00904             LOG_NG << "stored side in snapshot:\n" << new_side["save_id"] << std::endl;
00905             //add the units of the recall list
00906             foreach(const unit& u, i->recall_list()) {
00907                 config& new_unit = new_side.add_child("unit");
00908                 u.write(new_unit);
00909             }
00910         }
00911     }
00912     //add any players from starting_pos that do not have a team in the current scenario
00913     foreach (const config &player_cfg, gamestate_.starting_pos.child_range("player")) {
00914         if (side_ids.count(player_cfg["save_id"]) == 0) {
00915             LOG_NG << "stored inactive side in snapshot:\n" << player_cfg["save_id"] << std::endl;
00916             gamestate_.snapshot.add_child("side", player_cfg);
00917         }
00918     }
00919 }
00920 
00921 void playsingle_controller::store_gold(bool obs)
00922 {
00923     bool has_next_scenario = !gamestate_.classification().next_scenario.empty() &&
00924         gamestate_.classification().next_scenario != "null";
00925 
00926     std::ostringstream report;
00927     std::string title;
00928 
00929     if (obs) {
00930         title = _("Scenario Report");
00931     } else {
00932         persist_.end_transaction();
00933         title = _("Victory");
00934         report << "<b>" << _("You have emerged victorious!") << "</b>\n\n";
00935     }
00936 
00937     int persistent_teams = 0;
00938     foreach (const team &t, teams_) {
00939         if (t.persistent()) ++persistent_teams;
00940     }
00941 
00942     const end_level_data &end_level = get_end_level_data_const();
00943 
00944     if (persistent_teams > 0 && (has_next_scenario ||
00945         gamestate_.classification().campaign_type == "test"))
00946     {
00947         int finishing_bonus_per_turn =
00948             map_.villages().size() * game_config::village_income +
00949             game_config::base_income;
00950         int turns_left = std::max<int>(0, tod_manager_.number_of_turns() - turn());
00951         int finishing_bonus = (end_level.gold_bonus && turns_left > -1) ?
00952             finishing_bonus_per_turn * turns_left : 0;
00953         foreach (const team &t, teams_)
00954         {
00955             if (!t.persistent()) continue;
00956             int carryover_gold = div100rounded((t.gold() + finishing_bonus) * end_level.carryover_percentage);
00957             config::child_itors side_range = gamestate_.snapshot.child_range("side");
00958             config::child_iterator side_it = side_range.first;
00959 
00960             // Check if this side already exists in the snapshot.
00961             while (side_it != side_range.second) {
00962                 if ((*side_it)["save_id"] == t.save_id()) {
00963                     (*side_it)["gold"] = str_cast<int>(carryover_gold);
00964                     (*side_it)["gold_add"] = end_level.carryover_add;
00965                     (*side_it)["color"] = t.color();
00966                     (*side_it)["current_player"] = t.current_player();
00967                     (*side_it)["name"] = t.name();
00968                     break;
00969                 }
00970                 ++side_it;
00971             }
00972 
00973             // If it doesn't, add a new child.
00974             if (side_it == side_range.second) {
00975                 config &new_side = gamestate_.snapshot.add_child("side");
00976                 new_side["save_id"] = t.save_id();
00977                 new_side["gold"] = str_cast<int>(carryover_gold);
00978                 new_side["gold_add"] = end_level.carryover_add;
00979                 new_side["color"] = t.color();
00980                 new_side["current_player"] = t.current_player();
00981                 new_side["name"] = t.name();
00982             }
00983 
00984             // Only show the report for ourselves.
00985             if (!t.is_human()) continue;
00986 
00987             if (persistent_teams > 1) {
00988                 report << "\n<b>" << t.current_player() << "</b>\n";
00989             }
00990 
00991             report_victory(report, carryover_gold, t.gold(), finishing_bonus_per_turn, turns_left, finishing_bonus);
00992         }
00993     }
00994 
00995     if (end_level.carryover_report) {
00996         gui2::show_transient_message(gui_->video(), title, report.str(), "", true);
00997     }
00998 }
00999 
01000 bool playsingle_controller::can_execute_command(hotkey::HOTKEY_COMMAND command, int index) const
01001 {
01002     bool res = true;
01003     switch (command){
01004         case hotkey::HOTKEY_UNIT_HOLD_POSITION:
01005         case hotkey::HOTKEY_END_UNIT_TURN:
01006             return !browse_ && !linger_ && !events::commands_disabled;
01007         case hotkey::HOTKEY_RECRUIT:
01008         case hotkey::HOTKEY_REPEAT_RECRUIT:
01009         case hotkey::HOTKEY_RECALL:
01010             return (!browse_ || resources::whiteboard->is_active()) && !linger_ && !events::commands_disabled;
01011         case hotkey::HOTKEY_ENDTURN:
01012             return (!browse_ || linger_) && !events::commands_disabled;
01013 
01014         case hotkey::HOTKEY_DELAY_SHROUD:
01015             return !linger_ && (teams_[gui_->viewing_team()].uses_fog() || teams_[gui_->viewing_team()].uses_shroud())
01016             && !events::commands_disabled;
01017         case hotkey::HOTKEY_UPDATE_SHROUD:
01018             return !linger_
01019                 && player_number_ == gui_->viewing_side()
01020                 && !events::commands_disabled
01021                 && teams_[gui_->viewing_team()].auto_shroud_updates() == false;
01022 
01023         // Commands we can only do if in debug mode
01024         case hotkey::HOTKEY_CREATE_UNIT:
01025         case hotkey::HOTKEY_CHANGE_SIDE:
01026             return !events::commands_disabled && game_config::debug && map_.on_board(mouse_handler_.get_last_hex());
01027 
01028         case hotkey::HOTKEY_CLEAR_LABELS:
01029             res = !is_observer();
01030             break;
01031         case hotkey::HOTKEY_LABEL_TEAM_TERRAIN:
01032         case hotkey::HOTKEY_LABEL_TERRAIN: {
01033             const terrain_label *label = resources::screen->labels().get_label(mouse_handler_.get_last_hex());
01034             res = !events::commands_disabled && map_.on_board(mouse_handler_.get_last_hex())
01035                 && !gui_->shrouded(mouse_handler_.get_last_hex())
01036                 && !is_observer()
01037                 && (!label || !label->immutable());
01038             break;
01039         }
01040         case hotkey::HOTKEY_CONTINUE_MOVE: {
01041             if(browse_ || events::commands_disabled)
01042                 return false;
01043 
01044             if( (menu_handler_.current_unit() != units_.end())
01045                 && (menu_handler_.current_unit()->move_interrupted()))
01046                 return true;
01047             const unit_map::const_iterator i = units_.find(mouse_handler_.get_selected_hex());
01048             if (i == units_.end()) return false;
01049             return i->move_interrupted();
01050         }
01051         case hotkey::HOTKEY_WB_TOGGLE:
01052             return true;
01053         case hotkey::HOTKEY_WB_EXECUTE_ACTION:
01054         case hotkey::HOTKEY_WB_EXECUTE_ALL_ACTIONS:
01055             return resources::whiteboard->can_enable_execution_hotkeys();
01056         case hotkey::HOTKEY_WB_DELETE_ACTION:
01057             return resources::whiteboard->can_enable_modifier_hotkeys();
01058         case hotkey::HOTKEY_WB_BUMP_UP_ACTION:
01059         case hotkey::HOTKEY_WB_BUMP_DOWN_ACTION:
01060             return resources::whiteboard->can_enable_reorder_hotkeys();
01061         case hotkey::HOTKEY_WB_SUPPOSE_DEAD:
01062         {
01063             //@todo re-enable this once we figure out a decent UI for suppose_dead
01064             return false;
01065         }
01066 
01067         default: return play_controller::can_execute_command(command, index);
01068     }
01069     return res;
01070 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines

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