00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016 #include "playmp_controller.hpp"
00017
00018 #include "dialogs.hpp"
00019 #include "game_errors.hpp"
00020 #include "gettext.hpp"
00021 #include "log.hpp"
00022 #include "playturn.hpp"
00023 #include "sound.hpp"
00024 #include "upload_log.hpp"
00025
00026 #include <cassert>
00027
00028 #define LOG_NG LOG_STREAM(info, engine)
00029
00030 unsigned int playmp_controller::replay_last_turn_ = 0;
00031
00032 playmp_controller::playmp_controller(const config& level,
00033 game_state& state_of_game, const int ticks,
00034 const int num_turns, const config& game_config, CVideo& video,
00035 bool skip_replay, bool is_host)
00036 : playsingle_controller(level, state_of_game, ticks, num_turns,
00037 game_config, video, skip_replay)
00038 {
00039 beep_warning_time_ = 0;
00040 turn_data_ = NULL;
00041 is_host_ = is_host;
00042
00043 if ( replay_last_turn_ <= 1)
00044 {
00045 skip_replay_ = false;
00046 }
00047 }
00048
00049 playmp_controller::~playmp_controller() {
00050
00051 if(beep_warning_time_ < 0) {
00052 sound::stop_bell();
00053 }
00054 }
00055
00056 void playmp_controller::set_replay_last_turn(unsigned int turn){
00057 replay_last_turn_ = turn;
00058 }
00059
00060 void playmp_controller::clear_labels(){
00061 menu_handler_.clear_labels();
00062 }
00063
00064 void playmp_controller::speak(){
00065 menu_handler_.speak();
00066 }
00067
00068 void playmp_controller::whisper(){
00069 menu_handler_.whisper();
00070 }
00071
00072 void playmp_controller::shout(){
00073 menu_handler_.shout();
00074 }
00075
00076 void playmp_controller::play_side(const unsigned int team_index, bool save){
00077 do {
00078 player_type_changed_ = false;
00079 end_turn_ = false;
00080
00081 statistics::reset_turn_stats(player_number_);
00082
00083
00084
00085 if(current_team().is_human()) {
00086 LOG_NG << "is human...\n";
00087
00088
00089 beep_warning_time_ = 0;
00090
00091 try{
00092 before_human_turn(save);
00093 play_human_turn();
00094 after_human_turn();
00095 } catch(end_turn_exception& end_turn) {
00096 if (end_turn.redo == team_index) {
00097 player_type_changed_ = true;
00098
00099
00100 if (!teams_[team_index-1].is_human()) {
00101 int t = find_human_team_before(team_index);
00102
00103 if (t <= 0)
00104 t = gui_->get_playing_team() + 1;
00105
00106 gui_->set_team(t-1);
00107 gui_->recalculate_minimap();
00108 gui_->invalidate_all();
00109 gui_->draw(true,true);
00110 }
00111 }
00112 }
00113 LOG_NG << "human finished turn...\n";
00114 } else if(current_team().is_ai()) {
00115 play_ai_turn();
00116 } else if(current_team().is_network()) {
00117 play_network_turn();
00118 }
00119 } while (player_type_changed_);
00120
00121 }
00122
00123 void playmp_controller::before_human_turn(bool save){
00124 playsingle_controller::before_human_turn(save);
00125
00126 turn_data_ = new turn_info(gamestate_,status_,
00127 *gui_,map_,teams_,player_number_,units_,replay_sender_, undo_stack_);
00128 turn_data_->replay_error().attach_handler(this);
00129 turn_data_->host_transfer().attach_handler(this);
00130 }
00131
00132 bool playmp_controller::counting_down() {
00133 return beep_warning_time_ > 0;
00134 }
00135
00136 namespace {
00137 const int WARNTIME = 20000;
00138 unsigned timer_refresh = 0;
00139 const unsigned timer_refresh_rate = 50;
00140 }
00141
00142
00143 void playmp_controller::process(events::pump_info &info) {
00144 if(playmp_controller::counting_down()) {
00145 if(info.ticks(&timer_refresh, timer_refresh_rate)) {
00146 playmp_controller::think_about_countdown(info.ticks());
00147 }
00148 }
00149 }
00150
00151
00152 void playmp_controller::think_about_countdown(int ticks) {
00153 if(ticks >= beep_warning_time_) {
00154 const bool bell_on = preferences::turn_bell();
00155 if(bell_on || preferences::sound_on() || preferences::UI_sound_on()) {
00156 const int loop_ticks = WARNTIME - (ticks - beep_warning_time_);
00157 const int fadein_ticks = (loop_ticks > WARNTIME / 2) ? loop_ticks - WARNTIME / 2 : 0;
00158 sound::play_timer(game_config::sounds::timer_bell, loop_ticks, fadein_ticks);
00159 beep_warning_time_ = -1;
00160 }
00161 }
00162 }
00163
00164 void playmp_controller::play_human_turn(){
00165 int cur_ticks = SDL_GetTicks();
00166
00167 if ((!linger_) || (is_host_))
00168 gui_->enable_menu("endturn", true);
00169 while(!end_turn_) {
00170
00171 try {
00172 config cfg;
00173 const network::connection res = network::receive_data(cfg);
00174 std::deque<config> backlog;
00175
00176 if(res != network::null_connection) {
00177 try{
00178 if (turn_data_->process_network_data(cfg,res,backlog,skip_replay_) == turn_info::PROCESS_RESTART_TURN)
00179 {
00180 throw end_turn_exception(gui_->get_playing_team() + 1);
00181 }
00182 }
00183 catch (replay::error& e){
00184 process_oos(e.message);
00185 throw e;
00186 }
00187 }
00188
00189 play_slice();
00190 } catch(end_level_exception& e) {
00191 turn_data_->send_data();
00192 throw e;
00193 }
00194
00195 if (!linger_ && (current_team().countdown_time() > 0) && (level_["mp_countdown"] == "yes")) {
00196 SDL_Delay(1);
00197 const int ticks = SDL_GetTicks();
00198 int new_time = current_team().countdown_time()-maximum<int>(1,(ticks - cur_ticks));
00199 if (new_time > 0 ){
00200 current_team().set_countdown_time(new_time);
00201 cur_ticks = ticks;
00202 if(current_team().is_human() && !beep_warning_time_) {
00203 beep_warning_time_ = new_time - WARNTIME + ticks;
00204 }
00205 if(counting_down()) {
00206 think_about_countdown(ticks);
00207 }
00208 } else {
00209
00210
00211 const int action_increment = lexical_cast_default<int>(level_["mp_countdown_action_bonus"],0);
00212 if ( lexical_cast_default<int>(level_["mp_countdown_turn_bonus"],0) == 0
00213 && (action_increment == 0 || current_team().action_bonus_count() == 0)) {
00214
00215
00216
00217 current_team().set_countdown_time(10);
00218 } else {
00219 const int maxtime = lexical_cast_default<int>(level_["mp_countdown_reservoir_time"],0);
00220 int secs = lexical_cast_default<int>(level_["mp_countdown_turn_bonus"],0);
00221 secs += action_increment * current_team().action_bonus_count();
00222 current_team().set_action_bonus_count(0);
00223 secs = (secs > maxtime) ? maxtime : secs;
00224 current_team().set_countdown_time(1000 * secs);
00225 }
00226 recorder.add_countdown_update(current_team().countdown_time(),player_number_);
00227 recorder.end_turn();
00228 turn_data_->send_data();
00229
00230 throw end_turn_exception();
00231 }
00232 }
00233
00234 gui_->draw();
00235
00236 turn_data_->send_data();
00237 }
00238 menu_handler_.clear_undo_stack(player_number_);
00239 }
00240
00241 void playmp_controller::set_end_scenario_button()
00242 {
00243
00244 if (! is_host_) {
00245 gui::button* btn_end = gui_->find_button("button-endturn");
00246 btn_end->enable(false);
00247 }
00248 gui_->get_theme().refresh_title("button-endturn", _("End scenario"));
00249 gui_->invalidate_theme();
00250 gui_->redraw_everything();
00251 }
00252
00253 void playmp_controller::reset_end_scenario_button()
00254 {
00255
00256 gui_->get_theme().refresh_title2("button-endturn", "title");
00257 gui_->invalidate_theme();
00258 gui_->redraw_everything();
00259 gui_->set_game_mode(game_display::RUNNING);
00260 }
00261
00262 void playmp_controller::linger(upload_log& log)
00263 {
00264 LOG_NG << "beginning end-of-scenario linger\n";
00265 browse_ = true;
00266 linger_ = true;
00267
00268
00269 gui_->set_game_mode(game_display::LINGER_MP);
00270
00271
00272
00273 gamestate_.completion = "running";
00274
00275 for (unit_map::iterator u = units_.begin(); u != units_.end(); u++) {
00276 u->second.set_user_end_turn(true);
00277 }
00278
00279
00280 if(beep_warning_time_ < 0) {
00281 sound::stop_bell();
00282 }
00283 beep_warning_time_=-1;
00284
00285 set_end_scenario_button();
00286
00287
00288 gui_->set_team(0,true);
00289 gui_->recalculate_minimap();
00290 gui_->invalidate_all();
00291 gui_->draw(true,true);
00292
00293 bool quit;
00294 do {
00295 quit = true;
00296 try {
00297
00298 player_number_ = first_player_;
00299 turn_data_ = new turn_info(gamestate_, status_,
00300 *gui_,map_, teams_, player_number_,
00301 units_, replay_sender_, undo_stack_);
00302 turn_data_->replay_error().attach_handler(this);
00303 turn_data_->host_transfer().attach_handler(this);
00304
00305 play_human_turn();
00306 after_human_turn();
00307 LOG_NG << "finished human turn" << std::endl;
00308 } catch (game::load_game_exception&) {
00309 LOG_NG << "caught load-game-exception" << std::endl;
00310
00311 log.quit(status_.turn());
00312 throw;
00313 } catch (end_level_exception&) {
00314
00315
00316 LOG_NG << "caught end-level-exception" << std::endl;
00317 reset_end_scenario_button();
00318 throw;
00319 } catch (end_turn_exception&) {
00320
00321
00322
00323 LOG_NG << "caught end-turn-exception" << std::endl;
00324 quit = false;
00325 } catch (network::error&) {
00326 LOG_NG << "caught network-error-exception" << std::endl;
00327 quit = false;
00328 }
00329 } while (!quit);
00330
00331 reset_end_scenario_button();
00332
00333 LOG_NG << "ending end-of-scenario linger\n";
00334 }
00335
00336
00337 void playmp_controller::wait_for_upload()
00338 {
00339
00340
00341 assert(!is_host_);
00342
00343 const bool set_turn_data = (turn_data_ == 0);
00344 if(set_turn_data) {
00345 turn_data_ = new turn_info(gamestate_,status_,
00346 *gui_,map_,teams_,player_number_,units_,replay_sender_, undo_stack_);
00347 turn_data_->replay_error().attach_handler(this);
00348 turn_data_->host_transfer().attach_handler(this);
00349 }
00350
00351 while(true) {
00352 try {
00353 config cfg;
00354 const network::connection res = dialogs::network_receive_dialog(
00355 *gui_, _("Waiting for next scenario..."), cfg);
00356
00357 std::deque<config> backlog;
00358 if(res != network::null_connection) {
00359 try{
00360 if(turn_data_->process_network_data(cfg,res,backlog,skip_replay_)
00361 == turn_info::PROCESS_END_LINGER) {
00362 break;
00363 }
00364 }
00365 catch (replay::error& e){
00366 process_oos(e.message);
00367 throw e;
00368 }
00369 }
00370
00371 } catch(end_level_exception& e) {
00372 turn_data_->send_data();
00373 throw e;
00374 }
00375 }
00376
00377 if(set_turn_data) {
00378 delete turn_data_;
00379 turn_data_ = 0;
00380 }
00381 }
00382
00383 void playmp_controller::after_human_turn(){
00384 if ( level_["mp_countdown"] == "yes" ){
00385 const int action_increment = lexical_cast_default<int>(level_["mp_countdown_action_bonus"],0);
00386 const int maxtime = lexical_cast_default<int>(level_["mp_countdown_reservoir_time"],0);
00387 int secs = (current_team().countdown_time() / 1000) + lexical_cast_default<int>(level_["mp_countdown_turn_bonus"],0);
00388 secs += action_increment * current_team().action_bonus_count();
00389 current_team().set_action_bonus_count(0);
00390 secs = (secs > maxtime) ? maxtime : secs;
00391 current_team().set_countdown_time(1000 * secs);
00392 recorder.add_countdown_update(current_team().countdown_time(),player_number_);
00393 }
00394 end_turn_record();
00395
00396
00397 turn_data_->send_data();
00398 if (turn_data_ != NULL){
00399 turn_data_->replay_error().detach_handler(this);
00400 turn_data_->host_transfer().detach_handler(this);
00401 delete turn_data_;
00402 turn_data_ = NULL;
00403 }
00404
00405 playsingle_controller::after_human_turn();
00406 }
00407
00408 void playmp_controller::finish_side_turn(){
00409 play_controller::finish_side_turn();
00410
00411
00412 delete turn_data_;
00413 turn_data_ = NULL;
00414
00415
00416 if(beep_warning_time_ < 0) {
00417 sound::stop_bell();
00418 }
00419 }
00420
00421 void playmp_controller::play_network_turn(){
00422 LOG_NG << "is networked...\n";
00423
00424 browse_ = true;
00425 gui_->enable_menu("endturn", false);
00426 turn_info turn_data(gamestate_,status_,*gui_,
00427 map_,teams_,player_number_,units_, replay_sender_, undo_stack_);
00428 turn_data.replay_error().attach_handler(this);
00429 turn_data.host_transfer().attach_handler(this);
00430
00431 for(;;) {
00432
00433 bool have_data = false;
00434 config cfg;
00435
00436 network::connection from = network::null_connection;
00437
00438 if(data_backlog_.empty() == false) {
00439 have_data = true;
00440 cfg = data_backlog_.front();
00441 data_backlog_.pop_front();
00442 } else {
00443 from = network::receive_data(cfg);
00444 have_data = from != network::null_connection;
00445 }
00446
00447 if(have_data) {
00448 if (skip_replay_ && replay_last_turn_ <= status_.turn()){
00449 skip_replay_ = false;
00450 }
00451 try{
00452 const turn_info::PROCESS_DATA_RESULT result = turn_data.process_network_data(cfg,from,data_backlog_,skip_replay_);
00453 if(result == turn_info::PROCESS_RESTART_TURN) {
00454 player_type_changed_ = true;
00455 return;
00456 } else if(result == turn_info::PROCESS_END_TURN) {
00457 break;
00458 }
00459 }
00460 catch (replay::error e){
00461 process_oos(e.message);
00462 throw e;
00463 }
00464
00465 }
00466
00467 play_slice();
00468 turn_data.send_data();
00469 gui_->draw();
00470 }
00471
00472 turn_data.replay_error().detach_handler(this);
00473 turn_data.host_transfer().detach_handler(this);
00474 LOG_NG << "finished networked...\n";
00475 return;
00476 }
00477
00478 void playmp_controller::process_oos(const std::string& err_msg){
00479 std::stringstream temp_buf;
00480 std::vector<std::string> err_lines = utils::split(err_msg,'\n');
00481 temp_buf << _("The game is out of sync, and cannot continue. There are a number of reasons this could happen: this can occur if you or another player have modified their game settings. This may mean one of the players is attempting to cheat. It could also be due to a bug in the game, but this is less likely.\n\nDo you want to save an error log of your game?");
00482 if(!err_msg.empty()) {
00483 temp_buf << " \n \n";
00484 for(std::vector<std::string>::iterator i=err_lines.begin(); i!=err_lines.end(); i++)
00485 {
00486 temp_buf << "`#" << *i << '\n';
00487 }
00488 temp_buf << " \n";
00489 }
00490 menu_handler_.save_game(temp_buf.str(),gui::YES_NO, true);
00491 }
00492
00493 void playmp_controller::handle_generic_event(const std::string& name){
00494 turn_info turn_data(gamestate_,status_,*gui_,
00495 map_,teams_,player_number_,units_, replay_sender_, undo_stack_);
00496
00497 if (name == "ai_user_interact"){
00498 playsingle_controller::handle_generic_event(name);
00499 turn_data.send_data();
00500 }
00501 else if ((name == "ai_unit_recruited") || (name == "ai_unit_moved")
00502 || (name == "ai_enemy_attacked")){
00503 turn_data.sync_network();
00504 }
00505 else if (name == "network_replay_error"){
00506 process_oos(replay::last_replay_error);
00507 }
00508 else if (name == "host_transfer"){
00509 is_host_ = true;
00510 if (linger_){
00511 gui::button* btn_end = gui_->find_button("button-endturn");
00512 btn_end->enable(true);
00513 gui_->invalidate_theme();
00514 }
00515 }
00516 }
00517
00518 bool playmp_controller::can_execute_command(hotkey::HOTKEY_COMMAND command, int index) const
00519 {
00520 bool res = true;
00521 switch (command){
00522 case hotkey::HOTKEY_CLEAR_LABELS:
00523 res = !is_observer();
00524 case hotkey::HOTKEY_SPEAK:
00525 case hotkey::HOTKEY_SPEAK_ALLY:
00526 case hotkey::HOTKEY_SPEAK_ALL:
00527 res = res && network::nconnections() > 0;
00528 break;
00529 default:
00530 return playsingle_controller::can_execute_command(command, index);
00531 }
00532 return res;
00533 }