replay_controller.cpp

Go to the documentation of this file.
00001 /* $Id: replay_controller.cpp 53991 2012-04-25 04:20:12Z brilliand $ */
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 #include "global.hpp"
00018 
00019 #include "foreach.hpp"
00020 #include "game_end_exceptions.hpp"
00021 #include "game_events.hpp"
00022 #include "gettext.hpp"
00023 #include "log.hpp"
00024 #include "map_label.hpp"
00025 #include "replay.hpp"
00026 #include "replay_controller.hpp"
00027 #include "resources.hpp"
00028 #include "savegame.hpp"
00029 
00030 static lg::log_domain log_engine("engine");
00031 #define DBG_NG LOG_STREAM(debug, log_engine)
00032 
00033 static lg::log_domain log_replay("replay");
00034 #define DBG_REPLAY LOG_STREAM(debug, log_replay)
00035 #define LOG_REPLAY LOG_STREAM(info, log_replay)
00036 
00037 LEVEL_RESULT play_replay_level(const config& game_config,
00038         const config* level, CVideo& video, game_state& state_of_game)
00039 {
00040     try{
00041         const int ticks = SDL_GetTicks();
00042         int num_turns = (*level)["turns"].to_int(-1);
00043         DBG_NG << "creating objects... " << (SDL_GetTicks() - ticks) << "\n";
00044         replay_controller replaycontroller(*level, state_of_game, ticks, num_turns, game_config, video);
00045         DBG_NG << "created objects... " << (SDL_GetTicks() - replaycontroller.get_ticks()) << "\n";
00046         const events::command_disabler disable_commands;
00047 
00048         //replay event-loop
00049         for (;;){
00050             replaycontroller.play_slice();
00051         }
00052     }
00053     catch(end_level_exception&){
00054         DBG_NG << "play_replay_level: end_level_exception\n";
00055     }
00056 
00057     return VICTORY;
00058 }
00059 
00060 replay_controller::replay_controller(const config& level,
00061         game_state& state_of_game, const int ticks, const int num_turns,
00062         const config& game_config, CVideo& video) :
00063     play_controller(level, state_of_game, ticks, num_turns, game_config, video, false),
00064     teams_start_(),
00065     gamestate_start_(gamestate_),
00066     units_start_(),
00067     tod_manager_start_(level, num_turns),
00068     current_turn_(1),
00069     delay_(0),
00070     is_playing_(false),
00071     show_everything_(false),
00072     show_team_(state_of_game.classification().campaign_type == "multiplayer" ? 0 : 1)
00073 {
00074     units_start_ = units_;
00075     teams_start_ = teams_;
00076     tod_manager_start_ = tod_manager_;
00077 
00078     init();
00079     reset_replay();
00080 }
00081 
00082 replay_controller::~replay_controller()
00083 {
00084     //YogiHH
00085     //not absolutely sure if this is needed, but it makes me feel a lot better ;-)
00086     //feel free to delete this if it is not necessary
00087     gui_->get_theme().theme_reset_event().detach_handler(this);
00088     gui_->complete_redraw_event().detach_handler(this);
00089 }
00090 
00091 void replay_controller::init(){
00092     DBG_REPLAY << "in replay_controller::init()...\n";
00093 
00094     //guarantee the cursor goes back to 'normal' at the end of the level
00095     const cursor::setter cursor_setter(cursor::NORMAL);
00096     init_replay_display();
00097 }
00098 
00099 void replay_controller::init_gui(){
00100     DBG_NG << "Initializing GUI... " << (SDL_GetTicks() - ticks_) << "\n";
00101     play_controller::init_gui();
00102 
00103     if (show_team_)
00104         gui_->set_team(show_team_ - 1, show_everything_);
00105     else
00106         gui_->set_team(0, show_everything_);
00107 
00108     gui_->scroll_to_leader(units_, player_number_, display::WARP);
00109     update_locker lock_display((*gui_).video(),false);
00110     for(std::vector<team>::iterator t = teams_.begin(); t != teams_.end(); ++t) {
00111         t->reset_objectives_changed();
00112     }
00113 
00114     update_replay_ui();
00115 }
00116 
00117 void replay_controller::init_replay_display(){
00118     DBG_REPLAY << "initializing replay-display... " << (SDL_GetTicks() - ticks_) << "\n";
00119 
00120     rebuild_replay_theme();
00121     gui_->get_theme().theme_reset_event().attach_handler(this);
00122     gui_->complete_redraw_event().attach_handler(this);
00123     DBG_REPLAY << "done initializing replay-display... " << (SDL_GetTicks() - ticks_) << "\n";
00124 }
00125 
00126 void replay_controller::rebuild_replay_theme()
00127 {
00128     const config &theme_cfg = get_theme(game_config_, level_["theme"]);
00129     if (const config &res = theme_cfg.child("resolution"))
00130     {
00131         if (const config &replay_theme_cfg = res.child("replay"))
00132             gui_->get_theme().modify(replay_theme_cfg);
00133         gui_->get_theme().modify_label("time-icon", _ ("current local time"));
00134         //Make sure we get notified if the theme is redrawn completely. That way we have
00135         //a chance to restore the replay controls of the theme as well.
00136         gui_->invalidate_theme();
00137     }
00138 }
00139 
00140 gui::button* replay_controller::play_button()
00141 {
00142     return gui_->find_button("button-playreplay");
00143 }
00144 
00145 gui::button* replay_controller::stop_button()
00146 {
00147     return gui_->find_button("button-stopreplay");
00148 }
00149 
00150 gui::button* replay_controller::reset_button()
00151 {
00152     return gui_->find_button("button-resetreplay");
00153 }
00154 
00155 gui::button* replay_controller::play_turn_button()
00156 {
00157     return gui_->find_button("button-nextturn");
00158 }
00159 
00160 gui::button* replay_controller::play_side_button()
00161 {
00162     return gui_->find_button("button-nextside");
00163 }
00164 
00165 void replay_controller::update_replay_ui()
00166 {
00167     //check if we have all buttons - if someone messed with theme then some buttons may be missing
00168     //if any of the buttons is missing, we just disable every one
00169     if(!replay_ui_has_all_buttons()) {
00170         gui::button *play_b = play_button(), *stop_b = stop_button(),
00171                     *reset_b = reset_button(), *play_turn_b = play_turn_button(),
00172                     *play_side_b = play_side_button();
00173 
00174         if(play_b) {
00175             play_b->enable(false);
00176         }
00177 
00178         if(stop_b) {
00179             stop_b->enable(false);
00180         }
00181 
00182         if(reset_b) {
00183             reset_b->enable(false);
00184         }
00185 
00186         if(play_turn_b) {
00187             play_turn_b->enable(false);
00188         }
00189 
00190         if(play_side_b) {
00191             play_side_b->enable(false);
00192         }
00193     }
00194 }
00195 
00196 void replay_controller::replay_ui_playback_should_start()
00197 {
00198     if(!replay_ui_has_all_buttons())
00199         return;
00200 
00201     play_button()->enable(false);
00202     reset_button()->enable(false);
00203     play_turn_button()->enable(false);
00204     play_side_button()->enable(false);
00205 }
00206 
00207 void replay_controller::replay_ui_playback_should_stop()
00208 {
00209     if(!replay_ui_has_all_buttons())
00210         return;
00211 
00212     if(!recorder.at_end()) {
00213         play_button()->enable(true);
00214         reset_button()->enable(true);
00215         play_turn_button()->enable(true);
00216         play_side_button()->enable(true);
00217 
00218         play_button()->release();
00219         play_turn_button()->release();
00220         play_side_button()->release();
00221     } else {
00222         reset_button()->enable(true);
00223         stop_button()->enable(false);
00224     }
00225 
00226     if(!is_playing_) {
00227         //user interrupted
00228         stop_button()->release();
00229     }
00230 }
00231 
00232 void replay_controller::reset_replay_ui()
00233 {
00234     if(!replay_ui_has_all_buttons())
00235         return;
00236 
00237     play_button()->enable(true);
00238     stop_button()->enable(true);
00239     reset_button()->enable(true);
00240     play_turn_button()->enable(true);
00241     play_side_button()->enable(true);
00242 }
00243 
00244 
00245 void replay_controller::reset_replay(){
00246 
00247     gui_->clear_chat_messages();
00248     is_playing_ = false;
00249     player_number_ = 1;
00250     current_turn_ = 1;
00251     it_is_a_new_turn_ = true;
00252     skip_replay_ = false;
00253     tod_manager_= tod_manager_start_;
00254     recorder.start_replay();
00255     recorder.set_skip(false);
00256     units_ = units_start_;
00257     gamestate_ = gamestate_start_;
00258     teams_ = teams_start_;
00259     if (events_manager_ ){
00260         // NOTE: this double reset is required so that the new
00261         // instance of game_events::manager isn't created before the
00262         // old manager is actually destroyed (triggering an assertion
00263         // failure)
00264         events_manager_.reset();
00265         events_manager_.reset(new game_events::manager(level_));
00266     }
00267 
00268     gui_->labels().read(level_);
00269 
00270     statistics::fresh_stats();
00271     set_victory_when_enemies_defeated(level_["victory_when_enemies_defeated"].to_bool(true));
00272 
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     fire_prestart(true);
00279     init_gui();
00280     fire_start(true);
00281     update_gui();
00282 
00283     reset_replay_ui();
00284 }
00285 
00286 void replay_controller::stop_replay(){
00287     is_playing_ = false;
00288 }
00289 
00290 void replay_controller::replay_next_turn(){
00291     is_playing_ = true;
00292     replay_ui_playback_should_start();
00293 
00294     play_turn();
00295 
00296     if (!skip_replay_ || !is_playing_){
00297         gui_->scroll_to_leader(units_, player_number_,game_display::ONSCREEN,false);
00298     }
00299 
00300     replay_ui_playback_should_stop();
00301 }
00302 
00303 void replay_controller::replay_next_side(){
00304     is_playing_ = true;
00305     replay_ui_playback_should_start();
00306 
00307     play_side(player_number_ - 1, false);
00308 
00309     if (!skip_replay_ || !is_playing_) {
00310         gui_->scroll_to_leader(units_, player_number_,game_display::ONSCREEN,false);
00311     }
00312 
00313     replay_ui_playback_should_stop();
00314 }
00315 
00316 void replay_controller::process_oos(const std::string& msg) const
00317 {
00318     if (game_config::ignore_replay_errors) return;
00319 
00320     std::stringstream message;
00321     message << _("The replay is corrupt/out of sync. It might not make much sense to continue. Do you want to save the game?");
00322     message << "\n\n" << _("Error details:") << "\n\n" << msg;
00323 
00324     savegame::oos_savegame save(to_config());
00325     save.save_game_interactive(resources::screen->video(), message.str(), gui::YES_NO); // can throw end_level_exception
00326 }
00327 
00328 void replay_controller::replay_show_everything(){
00329     show_everything_ = true;
00330     show_team_ = 0;
00331     update_teams();
00332     update_gui();
00333 }
00334 
00335 void replay_controller::replay_show_each(){
00336     show_everything_ = false;
00337     show_team_ = 0;
00338     update_teams();
00339     update_gui();
00340 }
00341 
00342 void replay_controller::replay_show_team1(){
00343     show_everything_ = false;
00344     show_team_ = 1;
00345     update_teams();
00346     update_gui();
00347 }
00348 
00349 void replay_controller::replay_skip_animation(){
00350     skip_replay_ = !skip_replay_;
00351     recorder.set_skip(skip_replay_);
00352 }
00353 
00354 //move all sides till stop/end
00355 void replay_controller::play_replay(){
00356 
00357     if (recorder.at_end()){
00358         //shouldn't actually happen
00359         return;
00360     }
00361 
00362     try{
00363         is_playing_ = true;
00364         replay_ui_playback_should_start();
00365 
00366         DBG_REPLAY << "starting main loop\n" << (SDL_GetTicks() - ticks_) << "\n";
00367         for(; !recorder.at_end() && is_playing_; first_player_ = 1) {
00368             play_turn();
00369         }
00370 
00371         if (!is_playing_) {
00372             gui_->scroll_to_leader(units_, player_number_,game_display::ONSCREEN,false);
00373         }
00374     }
00375     catch(end_level_exception& e){
00376         if (e.result == QUIT) throw;
00377     }
00378 
00379     replay_ui_playback_should_stop();
00380 }
00381 
00382 //make all sides move, then stop
00383 void replay_controller::play_turn(){
00384 
00385     LOG_REPLAY << "turn: " << current_turn_ << "\n";
00386 
00387     gui_->new_turn();
00388     gui_->invalidate_game_status();
00389     events::raise_draw_event();
00390 
00391     bool last_team = false;
00392 
00393     while ( (!last_team) && (!recorder.at_end()) && is_playing_ ){
00394         last_team = static_cast<size_t>(player_number_) == teams_.size();
00395         play_side(player_number_ - 1, false);
00396         play_slice();
00397     }
00398 }
00399 
00400 //make only one side move
00401 void replay_controller::play_side(const unsigned int /*team_index*/, bool){
00402 
00403     DBG_REPLAY << "Status turn number: " << turn() << "\n";
00404     DBG_REPLAY << "Replay_Controller turn number: " << current_turn_ << "\n";
00405     DBG_REPLAY << "Player number: " << player_number_ << "\n";
00406 
00407     try{
00408         // If a side is empty skip over it.
00409         if (!current_team().is_empty()) {
00410             statistics::reset_turn_stats(current_team().save_id());
00411 
00412             play_controller::init_side(player_number_ - 1, true);
00413 
00414             DBG_REPLAY << "doing replay " << player_number_ << "\n";
00415             do_replay(player_number_);
00416 
00417             finish_side_turn();
00418         }
00419 
00420         player_number_++;
00421 
00422         if (static_cast<size_t>(player_number_) > teams_.size()) {
00423             finish_turn();
00424             tod_manager_.next_turn();
00425             it_is_a_new_turn_ = true;
00426             player_number_ = 1;
00427             current_turn_++;
00428             gui_->new_turn();
00429         }
00430 
00431         // This is necessary for replays in order to show possible movements.
00432         foreach (unit &u, units_) {
00433             if (u.side() == player_number_) {
00434                 u.new_turn();
00435             }
00436         }
00437 
00438         update_teams();
00439         update_gui();
00440     }
00441     catch(end_level_exception& e){
00442         //VICTORY/DEFEAT end_level_exception shall not return to title screen
00443         if (e.result != VICTORY && e.result != DEFEAT) throw;
00444     }
00445 }
00446 
00447 void replay_controller::update_teams(){
00448 
00449     int next_team = player_number_;
00450     if(static_cast<size_t>(next_team) > teams_.size()) {
00451         next_team = 1;
00452     }
00453 
00454     if (!show_team_) {
00455         gui_->set_team(next_team - 1, show_everything_);
00456     } else {
00457         gui_->set_team(show_team_ - 1, show_everything_);
00458     }
00459 
00460     clear_shroud(next_team);
00461 
00462     gui_->set_playing_team(next_team - 1);
00463     gui_->invalidate_all();
00464 }
00465 
00466 void replay_controller::update_gui(){
00467     (*gui_).recalculate_minimap();
00468     (*gui_).redraw_minimap();
00469     (*gui_).invalidate_all();
00470     events::raise_draw_event();
00471     (*gui_).draw();
00472 }
00473 
00474 void replay_controller::preferences(){
00475     play_controller::preferences();
00476     update_gui();
00477 }
00478 
00479 void replay_controller::show_statistics(){
00480     menu_handler_.show_statistics(gui_->playing_team()+1);
00481 }
00482 
00483 void replay_controller::handle_generic_event(const std::string& name){
00484 
00485     if( name == "completely_redrawn" ) {
00486         update_replay_ui();
00487 
00488         gui::button* skip_animation_button = gui_->find_button("skip-animation");
00489         if(skip_animation_button) {
00490             skip_animation_button->set_check(skip_replay_);
00491         }
00492     } else {
00493         rebuild_replay_theme();
00494     }
00495 }
00496 
00497 bool replay_controller::can_execute_command(hotkey::HOTKEY_COMMAND command, int index) const
00498 {
00499     bool result = play_controller::can_execute_command(command,index);
00500 
00501     switch(command) {
00502 
00503     //commands we can always do
00504     case hotkey::HOTKEY_PLAY_REPLAY:
00505     case hotkey::HOTKEY_RESET_REPLAY:
00506     case hotkey::HOTKEY_STOP_REPLAY:
00507     case hotkey::HOTKEY_REPLAY_NEXT_TURN:
00508     case hotkey::HOTKEY_REPLAY_NEXT_SIDE:
00509     case hotkey::HOTKEY_REPLAY_SHOW_EVERYTHING:
00510     case hotkey::HOTKEY_REPLAY_SHOW_EACH:
00511     case hotkey::HOTKEY_REPLAY_SHOW_TEAM1:
00512     case hotkey::HOTKEY_REPLAY_SKIP_ANIMATION:
00513     case hotkey::HOTKEY_SAVE_GAME:
00514     case hotkey::HOTKEY_SAVE_REPLAY:
00515     case hotkey::HOTKEY_CHAT_LOG:
00516         return true;
00517 
00518     default:
00519         return result;
00520     }
00521 }
 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