00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017 #include "playmp_controller.hpp"
00018
00019 #include "dialogs.hpp"
00020 #include "foreach.hpp"
00021 #include "game_end_exceptions.hpp"
00022 #include "gettext.hpp"
00023 #include "log.hpp"
00024 #include "playturn.hpp"
00025 #include "preferences.hpp"
00026 #include "resources.hpp"
00027 #include "savegame.hpp"
00028 #include "sound.hpp"
00029 #include "formula_string_utils.hpp"
00030 #include "whiteboard/manager.hpp"
00031
00032 static lg::log_domain log_engine("engine");
00033 #define LOG_NG LOG_STREAM(info, log_engine)
00034
00035 unsigned int playmp_controller::replay_last_turn_ = 0;
00036
00037 playmp_controller::playmp_controller(const config& level,
00038 game_state& state_of_game, const int ticks,
00039 const int num_turns, const config& game_config, CVideo& video,
00040 bool skip_replay, bool is_host) :
00041 playsingle_controller(level, state_of_game, ticks, num_turns,
00042 game_config, video, skip_replay),
00043 turn_data_(NULL),
00044 beep_warning_time_(0),
00045 network_processing_stopped_(false)
00046 {
00047 is_host_ = is_host;
00048
00049 if ( replay_last_turn_ <= 1)
00050 {
00051 skip_replay_ = false;
00052 }
00053 }
00054
00055 playmp_controller::~playmp_controller() {
00056
00057 if(beep_warning_time_ < 0) {
00058 sound::stop_bell();
00059 }
00060 }
00061
00062 void playmp_controller::set_replay_last_turn(unsigned int turn){
00063 replay_last_turn_ = turn;
00064 }
00065
00066 void playmp_controller::speak(){
00067 menu_handler_.speak();
00068 }
00069
00070 void playmp_controller::whisper(){
00071 menu_handler_.whisper();
00072 }
00073
00074 void playmp_controller::shout(){
00075 menu_handler_.shout();
00076 }
00077
00078 void playmp_controller::start_network(){
00079 network_processing_stopped_ = false;
00080 LOG_NG << "network processing activated again";
00081 }
00082
00083 void playmp_controller::stop_network(){
00084 network_processing_stopped_ = true;
00085 LOG_NG << "network processing stopped";
00086 }
00087
00088 void playmp_controller::play_side(const unsigned int side_number, bool save)
00089 {
00090 utils::string_map player;
00091 player["name"] = current_team().current_player();
00092 std::string turn_notification_msg = _("$name has taken control");
00093 turn_notification_msg = utils::interpolate_variables_into_string(turn_notification_msg, &player);
00094 gui_->send_notification(_("Turn changed"), turn_notification_msg);
00095
00096 do {
00097 player_type_changed_ = false;
00098 if (!skip_next_turn_)
00099 end_turn_ = false;
00100
00101 statistics::reset_turn_stats(teams_[side_number-1].save_id());
00102
00103
00104
00105 if(current_team().is_human()) {
00106 LOG_NG << "is human...\n";
00107
00108
00109 try{
00110 before_human_turn(save);
00111 play_human_turn();
00112 after_human_turn();
00113 } catch(end_turn_exception& end_turn) {
00114 if (end_turn.redo == side_number) {
00115 player_type_changed_ = true;
00116
00117
00118 if (!teams_[side_number-1].is_human()) {
00119 browse_ = true;
00120 int t = find_human_team_before(side_number);
00121
00122 if (t <= 0)
00123 t = gui_->playing_side();
00124
00125 update_gui_to_player(t-1);
00126 }
00127 } else {
00128 after_human_turn();
00129 }
00130 }
00131 LOG_NG << "human finished turn...\n";
00132 } else if(current_team().is_ai()) {
00133 play_ai_turn();
00134 } else if(current_team().is_network()) {
00135 play_network_turn();
00136 }
00137 } while (player_type_changed_);
00138
00139
00140 skip_next_turn_ = false;
00141 }
00142
00143 void playmp_controller::before_human_turn(bool save){
00144 LOG_NG << "playmp::before_human_turn...\n";
00145 playsingle_controller::before_human_turn(save);
00146
00147 init_turn_data();
00148 }
00149
00150 bool playmp_controller::counting_down() {
00151 return beep_warning_time_ > 0;
00152 }
00153
00154 namespace {
00155 const int WARNTIME = 20000;
00156 unsigned timer_refresh = 0;
00157 const unsigned timer_refresh_rate = 50;
00158 }
00159
00160
00161 void playmp_controller::process(events::pump_info &info) {
00162 if(playmp_controller::counting_down()) {
00163 if(info.ticks(&timer_refresh, timer_refresh_rate)) {
00164 playmp_controller::think_about_countdown(info.ticks());
00165 }
00166 }
00167 }
00168
00169 void playmp_controller::reset_countdown()
00170 {
00171 if (beep_warning_time_ < 0)
00172 sound::stop_bell();
00173 beep_warning_time_ = 0;
00174 }
00175
00176
00177
00178 void playmp_controller::think_about_countdown(int ticks) {
00179 if(ticks >= beep_warning_time_) {
00180 const bool bell_on = preferences::turn_bell();
00181 if(bell_on || preferences::sound_on() || preferences::UI_sound_on()) {
00182 const int loop_ticks = WARNTIME - (ticks - beep_warning_time_);
00183 const int fadein_ticks = (loop_ticks > WARNTIME / 2) ? loop_ticks - WARNTIME / 2 : 0;
00184 sound::play_timer(game_config::sounds::timer_bell, loop_ticks, fadein_ticks);
00185 beep_warning_time_ = -1;
00186 }
00187 }
00188 }
00189
00190 namespace {
00191 struct command_disabled_resetter
00192 {
00193 command_disabled_resetter() : val_(events::commands_disabled) {}
00194 ~command_disabled_resetter() { events::commands_disabled = val_; }
00195 private:
00196 int val_;
00197 };
00198 }
00199
00200 void playmp_controller::play_human_turn(){
00201 LOG_NG << "playmp::play_human_turn...\n";
00202 command_disabled_resetter reset_commands;
00203 int cur_ticks = SDL_GetTicks();
00204 show_turn_dialog();
00205 execute_gotos();
00206
00207 if ((!linger_) || (is_host_))
00208 gui_->enable_menu("endturn", true);
00209 while(!end_turn_) {
00210
00211 try {
00212 config cfg;
00213 const network::connection res = network::receive_data(cfg);
00214 std::deque<config> backlog;
00215
00216 if(res != network::null_connection) {
00217 if (turn_data_->process_network_data(cfg, res, backlog, skip_replay_) == turn_info::PROCESS_RESTART_TURN)
00218 {
00219
00220 if (!undo_stack_.empty())
00221 {
00222 font::floating_label flabel(_("Undoing moves not yet transmitted to the server."));
00223
00224 SDL_Color color = {255,255,255,255};
00225 flabel.set_color(color);
00226 SDL_Rect rect = gui_->map_area();
00227 flabel.set_position(rect.w/2, rect.h/2);
00228 flabel.set_lifetime(150);
00229 flabel.set_clip_rect(rect);
00230
00231 font::add_floating_label(flabel);
00232 }
00233
00234 while(!undo_stack_.empty())
00235 menu_handler_.undo(gui_->playing_side());
00236 throw end_turn_exception(gui_->playing_side());
00237 }
00238 }
00239
00240 play_slice();
00241 check_end_level();
00242
00243 resources::whiteboard->continue_execute_all();
00244 } catch(const end_level_exception&) {
00245 turn_data_->send_data();
00246 throw;
00247 }
00248
00249 if (!linger_ && (current_team().countdown_time() > 0) && gamestate_.mp_settings().mp_countdown) {
00250 SDL_Delay(1);
00251 const int ticks = SDL_GetTicks();
00252 int new_time = current_team().countdown_time()-std::max<int>(1,(ticks - cur_ticks));
00253 if (new_time > 0 ){
00254 current_team().set_countdown_time(new_time);
00255 cur_ticks = ticks;
00256 if(current_team().is_human() && !beep_warning_time_) {
00257 beep_warning_time_ = new_time - WARNTIME + ticks;
00258 }
00259 if(counting_down()) {
00260 think_about_countdown(ticks);
00261 }
00262 } else {
00263
00264
00265 const int action_increment = gamestate_.mp_settings().mp_countdown_action_bonus;
00266 if ( (gamestate_.mp_settings().mp_countdown_turn_bonus == 0 )
00267 && (action_increment == 0 || current_team().action_bonus_count() == 0)) {
00268
00269
00270
00271 current_team().set_countdown_time(10);
00272 } else {
00273 const int maxtime = gamestate_.mp_settings().mp_countdown_reservoir_time;
00274 int secs = gamestate_.mp_settings().mp_countdown_turn_bonus;
00275 secs += action_increment * current_team().action_bonus_count();
00276 current_team().set_action_bonus_count(0);
00277 secs = (secs > maxtime) ? maxtime : secs;
00278 current_team().set_countdown_time(1000 * secs);
00279 }
00280 turn_data_->send_data();
00281
00282 if (!rand_rng::has_new_seed_callback()) {
00283 throw end_turn_exception();
00284 }
00285 }
00286 }
00287
00288 gui_->draw();
00289
00290 turn_data_->send_data();
00291 }
00292 menu_handler_.clear_undo_stack(player_number_);
00293 }
00294
00295 void playmp_controller::set_end_scenario_button()
00296 {
00297
00298 if (! is_host_) {
00299 gui::button* btn_end = gui_->find_button("button-endturn");
00300 btn_end->enable(false);
00301 }
00302 gui_->get_theme().refresh_title2("button-endturn", "title2");
00303 gui_->invalidate_theme();
00304 gui_->redraw_everything();
00305 }
00306
00307 void playmp_controller::reset_end_scenario_button()
00308 {
00309
00310 gui_->get_theme().refresh_title2("button-endturn", "title");
00311 gui_->invalidate_theme();
00312 gui_->redraw_everything();
00313 gui_->set_game_mode(game_display::RUNNING);
00314 }
00315
00316 void playmp_controller::linger()
00317 {
00318 LOG_NG << "beginning end-of-scenario linger\n";
00319 browse_ = true;
00320 linger_ = true;
00321
00322
00323 gui_->set_game_mode(game_display::LINGER_MP);
00324
00325
00326
00327 gamestate_.classification().completion = "running";
00328
00329 foreach (unit &u, units_) {
00330 u.set_user_end_turn(true);
00331 }
00332
00333
00334 reset_countdown();
00335
00336 set_end_scenario_button();
00337
00338 if ( get_end_level_data_const().reveal_map ) {
00339
00340
00341 update_gui_to_player(gui_->viewing_team(), true);
00342 }
00343 bool quit;
00344 do {
00345 quit = true;
00346 try {
00347
00348 player_number_ = first_player_;
00349 init_turn_data();
00350
00351 end_turn_ = false;
00352 play_human_turn();
00353 turn_over_ = true;
00354 after_human_turn();
00355 LOG_NG << "finished human turn" << std::endl;
00356 } catch (game::load_game_exception&) {
00357 LOG_NG << "caught load-game-exception" << std::endl;
00358
00359 throw;
00360 } catch (end_level_exception&) {
00361
00362
00363 LOG_NG << "caught end-level-exception" << std::endl;
00364 reset_end_scenario_button();
00365 throw;
00366 } catch (end_turn_exception&) {
00367
00368
00369
00370 LOG_NG << "caught end-turn-exception" << std::endl;
00371 quit = false;
00372 } catch (network::error&) {
00373 LOG_NG << "caught network-error-exception" << std::endl;
00374 quit = false;
00375 }
00376 } while (!quit);
00377
00378 reset_end_scenario_button();
00379
00380 LOG_NG << "ending end-of-scenario linger\n";
00381 }
00382
00383 void playmp_controller::wait_for_upload()
00384 {
00385
00386
00387 assert(!is_host_);
00388
00389 const bool set_turn_data = (turn_data_ == 0);
00390 if(set_turn_data) {
00391 init_turn_data();
00392 }
00393
00394 while(true) {
00395 try {
00396 config cfg;
00397 const network::connection res = dialogs::network_receive_dialog(
00398 *gui_, _("Waiting for next scenario..."), cfg);
00399
00400 std::deque<config> backlog;
00401 if(res != network::null_connection) {
00402 if (turn_data_->process_network_data(cfg, res, backlog, skip_replay_)
00403 == turn_info::PROCESS_END_LINGER) {
00404 break;
00405 }
00406 }
00407
00408 } catch(const end_level_exception&) {
00409 turn_data_->send_data();
00410 throw;
00411 }
00412 }
00413
00414 if(set_turn_data) {
00415 delete turn_data_;
00416 turn_data_ = 0;
00417 }
00418 }
00419
00420 void playmp_controller::after_human_turn(){
00421 if ( gamestate_.mp_settings().mp_countdown ){
00422 const int action_increment = gamestate_.mp_settings().mp_countdown_action_bonus;
00423 const int maxtime = gamestate_.mp_settings().mp_countdown_reservoir_time;
00424 int secs = (current_team().countdown_time() / 1000) + gamestate_.mp_settings().mp_countdown_turn_bonus;
00425 secs += action_increment * current_team().action_bonus_count();
00426 current_team().set_action_bonus_count(0);
00427 secs = (secs > maxtime) ? maxtime : secs;
00428 current_team().set_countdown_time(1000 * secs);
00429 recorder.add_countdown_update(current_team().countdown_time(),player_number_);
00430 }
00431 LOG_NG << "playmp::after_human_turn...\n";
00432 end_turn_record();
00433
00434
00435 if (turn_data_ == NULL) init_turn_data();
00436
00437
00438 turn_data_->send_data();
00439 playsingle_controller::after_human_turn();
00440 if (turn_data_ != NULL){
00441 turn_data_->host_transfer().detach_handler(this);
00442 delete turn_data_;
00443 turn_data_ = NULL;
00444 }
00445
00446 }
00447
00448 void playmp_controller::finish_side_turn(){
00449 play_controller::finish_side_turn();
00450
00451
00452 delete turn_data_;
00453 turn_data_ = NULL;
00454
00455
00456 reset_countdown();
00457
00458
00459 rand_rng::clear_new_seed_callback();
00460 }
00461
00462 void playmp_controller::play_network_turn(){
00463 LOG_NG << "is networked...\n";
00464
00465 gui_->enable_menu("endturn", false);
00466 turn_info turn_data(player_number_, replay_sender_);
00467 turn_data.host_transfer().attach_handler(this);
00468
00469 for(;;) {
00470
00471 if (!network_processing_stopped_){
00472 bool have_data = false;
00473 config cfg;
00474
00475 network::connection from = network::null_connection;
00476
00477 if(data_backlog_.empty() == false) {
00478 have_data = true;
00479 cfg = data_backlog_.front();
00480 data_backlog_.pop_front();
00481 } else {
00482 from = network::receive_data(cfg);
00483 have_data = from != network::null_connection;
00484 }
00485
00486 if(have_data) {
00487 if (skip_replay_ && replay_last_turn_ <= turn()){
00488 skip_replay_ = false;
00489 }
00490 const turn_info::PROCESS_DATA_RESULT result = turn_data.process_network_data(cfg, from, data_backlog_, skip_replay_);
00491 if (result == turn_info::PROCESS_RESTART_TURN) {
00492 update_gui_to_player(player_number_ - 1);
00493 player_type_changed_ = true;
00494 return;
00495 } else if (result == turn_info::PROCESS_END_TURN) {
00496 break;
00497 }
00498 }
00499 }
00500
00501 play_slice();
00502 check_end_level();
00503
00504 if (!network_processing_stopped_){
00505 turn_data.send_data();
00506 }
00507
00508 gui_->draw();
00509 }
00510
00511 turn_data.host_transfer().detach_handler(this);
00512 LOG_NG << "finished networked...\n";
00513 return;
00514 }
00515
00516 void playmp_controller::init_turn_data() {
00517 turn_data_ = new turn_info(player_number_, replay_sender_);
00518 turn_data_->host_transfer().attach_handler(this);
00519 }
00520
00521 void playmp_controller::process_oos(const std::string& err_msg) const {
00522
00523 config cfg;
00524 config& info = cfg.add_child("info");
00525 info["type"] = "termination";
00526 info["condition"] = "out of sync";
00527 network::send_data(cfg, 0);
00528
00529 std::stringstream temp_buf;
00530 std::vector<std::string> err_lines = utils::split(err_msg,'\n');
00531 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?");
00532 if(!err_msg.empty()) {
00533 temp_buf << " \n \n";
00534 for(std::vector<std::string>::iterator i=err_lines.begin(); i!=err_lines.end(); ++i)
00535 {
00536 temp_buf << *i << '\n';
00537 }
00538 temp_buf << " \n";
00539 }
00540
00541 savegame::oos_savegame save(to_config());
00542 save.save_game_interactive(resources::screen->video(), temp_buf.str(), gui::YES_NO);
00543 }
00544
00545 void playmp_controller::handle_generic_event(const std::string& name){
00546 turn_info turn_data(player_number_, replay_sender_);
00547
00548 if (name == "ai_user_interact"){
00549 playsingle_controller::handle_generic_event(name);
00550 turn_data.send_data();
00551 }
00552 else if ((name == "ai_gamestate_changed") || (name == "ai_sync_network")){
00553 turn_data.sync_network();
00554 }
00555 else if (name == "host_transfer"){
00556 is_host_ = true;
00557 if (linger_){
00558 gui::button* btn_end = gui_->find_button("button-endturn");
00559 btn_end->enable(true);
00560 gui_->invalidate_theme();
00561 }
00562 }
00563 if (end_turn_) {
00564 throw end_turn_exception();
00565 }
00566 }
00567
00568 bool playmp_controller::can_execute_command(hotkey::HOTKEY_COMMAND command, int index) const
00569 {
00570 bool res = true;
00571 switch (command){
00572 case hotkey::HOTKEY_SPEAK:
00573 case hotkey::HOTKEY_SPEAK_ALLY:
00574 case hotkey::HOTKEY_SPEAK_ALL:
00575 res = network::nconnections() > 0;
00576 break;
00577 case hotkey::HOTKEY_START_NETWORK:
00578 case hotkey::HOTKEY_STOP_NETWORK:
00579 res = is_observer();
00580 break;
00581 case hotkey::HOTKEY_STOP_REPLAY:
00582 if (is_observer()){
00583 network_processing_stopped_ = true;
00584 LOG_NG << "network processing stopped";
00585 }
00586 break;
00587 default:
00588 return playsingle_controller::can_execute_command(command, index);
00589 }
00590 return res;
00591 }