possible_end_play_signal playmp_controller::play_idle_loop() { LOG_NG << "playmp::play_human_turn...\n"; remove_blindfold(); while (!end_turn_) { turn_info_send send_safe(turn_data_); config cfg; if(network_reader_.read(cfg)) { turn_info::PROCESS_DATA_RESULT res; HANDLE_END_PLAY_SIGNAL( res = turn_data_.process_network_data(cfg, skip_replay_) ); if (res == turn_info::PROCESS_RESTART_TURN || res == turn_info::PROCESS_RESTART_TURN_TEMPORARY_LOCAL) { end_turn_struct ets = {static_cast<unsigned>(gui_->playing_side())}; return possible_end_play_signal(ets); //throw end_turn_exception(gui_->playing_side()); } } HANDLE_END_PLAY_SIGNAL ( play_slice() ); HANDLE_END_PLAY_SIGNAL ( check_end_level() ); if (!linger_) { SDL_Delay(1); } gui_->draw(); } return boost::none; }
possible_end_play_signal playmp_controller::play_network_turn(){ LOG_NG << "is networked...\n"; end_turn_enable(false); turn_data_.send_data(); for(;;) { if (!network_processing_stopped_){ config cfg; if(network_reader_.read(cfg)) { if (replay_last_turn_ <= turn()){ if (skip_replay_) { skip_replay_ = false; } } turn_info::PROCESS_DATA_RESULT result; HANDLE_END_PLAY_SIGNAL ( result = turn_data_.process_network_data(cfg, skip_replay_) ); if(player_type_changed_ == true) { //we received a player change/quit during waiting in get_user_choice/synced_context::pull_remote_user_input return boost::none; } if (result == turn_info::PROCESS_RESTART_TURN || result == turn_info::PROCESS_RESTART_TURN_TEMPORARY_LOCAL) { player_type_changed_ = true; return boost::none; } else if (result == turn_info::PROCESS_END_TURN) { break; } } /* we might have data left in replay that we recieved during prestart events. (or maybe other events.) */ else if(!recorder.at_end()) { bool was_skipping = recorder.is_skipping(); recorder.set_skip(skip_replay_); if(do_replay() == REPLAY_FOUND_END_TURN) { break; } recorder.set_skip(was_skipping); } } HANDLE_END_PLAY_SIGNAL( play_slice() ); HANDLE_END_PLAY_SIGNAL( check_end_level() ); if (!network_processing_stopped_){ turn_data_.send_data(); } gui_->draw(); } LOG_NG << "finished networked...\n"; return boost::none; }
possible_end_play_signal playsingle_controller::play_human_turn() { show_turn_dialog(); HANDLE_END_PLAY_SIGNAL( execute_gotos() ); end_turn_enable(true); while(!end_turn_) { HANDLE_END_PLAY_SIGNAL( play_slice() ); HANDLE_END_PLAY_SIGNAL( check_end_level() ); gui_->draw(); } return boost::none; }
possible_end_play_signal playsingle_controller::check_time_over() { bool b = gamestate_.tod_manager_.next_turn(*resources::gamedata); it_is_a_new_turn_ = true; if(!b) { LOG_NG << "firing time over event...\n"; game_events::fire("time over"); LOG_NG << "done firing time over event...\n"; //if turns are added while handling 'time over' event if (gamestate_.tod_manager_.is_time_left()) { return boost::none; } if(non_interactive()) { LOG_AIT << "time over (draw)\n"; ai_testing::log_draw(); } HANDLE_END_PLAY_SIGNAL( check_victory() ); get_end_level_data().proceed_to_next_level = false; end_level_struct els = {DEFEAT}; return possible_end_play_signal (els); //throw end_level_exception(DEFEAT); } return boost::none; }
possible_end_play_signal replay_controller::replay_next_side(){ is_playing_ = true; replay_ui_playback_should_start(); HANDLE_END_PLAY_SIGNAL( play_side() ); while (current_team().is_empty()) { HANDLE_END_PLAY_SIGNAL( play_side() ); } if (!skip_replay_ || !is_playing_) { gui_->scroll_to_leader(player_number_,game_display::ONSCREEN,false); } replay_ui_playback_should_stop(); return boost::none; }
possible_end_play_signal playsingle_controller::play_scenario_init(end_level_data & /*eld*/, bool & past_prestart) { // At the beginning of the scenario, save a snapshot as replay_start if(saved_game_.replay_start().empty()) { saved_game_.replay_start() = to_config(); } HANDLE_END_PLAY_SIGNAL( fire_preload() ); replaying_ = (recorder.at_end() == false); if(!loading_game_ ) { if(replaying_) { //can this codepath be reached ? //note this when we are entering an mp game and see the 'replay' of the game //this path is not reached because we receive the replay later config* pstart = recorder.get_next_action(); assert(pstart->has_child("start")); } else { assert(recorder.empty()); recorder.add_start(); recorder.get_next_action(); } //we can only use a set_scontext_synced with a non empty recorder. set_scontext_synced sync; HANDLE_END_PLAY_SIGNAL( fire_prestart() ); init_gui(); past_prestart = true; LOG_NG << "first_time..." << (recorder.is_skipping() ? "skipping" : "no skip") << "\n"; events::raise_draw_event(); HANDLE_END_PLAY_SIGNAL( fire_start(true) ); gui_->recalculate_minimap(); } else { init_gui(); past_prestart = true; events::raise_draw_event(); HANDLE_END_PLAY_SIGNAL( fire_start(false) ); gui_->recalculate_minimap(); } return boost::none; }
possible_end_play_signal playsingle_controller::play_idle_loop() { while(!end_turn_) { HANDLE_END_PLAY_SIGNAL( play_slice() ); gui_->draw(); SDL_Delay(10); } return boost::none; }
possible_end_play_signal play_replay_level_main_loop(replay_controller & replaycontroller, bool & is_unit_test) { if (is_unit_test) { return replaycontroller.try_run_to_completion(); } for (;;){ HANDLE_END_PLAY_SIGNAL( replaycontroller.play_slice() ); } }
possible_end_play_signal replay_controller::try_run_to_completion() { for (;;) { HANDLE_END_PLAY_SIGNAL( play_slice() ); if (recorder.at_end()) { return boost::none; } else { if (!is_playing_) { PROPOGATE_END_PLAY_SIGNAL( play_replay() ); } } } }
//make all sides move, then stop possible_end_play_signal replay_controller::play_turn(){ LOG_REPLAY << "turn: " << current_turn_ << "\n"; gui_->new_turn(); gui_->invalidate_game_status(); events::raise_draw_event(); bool last_team = false; while ( (!last_team) && (!recorder.at_end()) && is_playing_ ){ last_team = static_cast<size_t>(player_number_) == gamestate_.board_.teams().size(); PROPOGATE_END_PLAY_SIGNAL( play_side() ); HANDLE_END_PLAY_SIGNAL( play_slice() ); } return boost::none; }
possible_end_play_signal playsingle_controller::before_human_turn() { log_scope("player turn"); browse_ = false; linger_ = false; HANDLE_END_PLAY_SIGNAL( ai::manager::raise_turn_started() ); //This line throws exception from here: https://github.com/wesnoth/wesnoth/blob/ac96a2b91b3276e20b682210617cf87d1e0d366a/src/playsingle_controller.cpp#L954 if(do_autosaves_ && level_result_ == NONE) { savegame::autosave_savegame save(saved_game_, *gui_, to_config(), preferences::save_compression_format()); save.autosave(game_config::disable_autosave, preferences::autosavemax(), preferences::INFINITE_AUTO_SAVES); } if(preferences::turn_bell() && level_result_ == NONE) { sound::play_bell(game_config::sounds::turn_bell); } return boost::none; }
possible_end_play_signal playsingle_controller::play_side() { //check for team-specific items in the scenario gui_->parse_team_overlays(); HANDLE_END_PLAY_SIGNAL( maybe_do_init_side(false) ); //flag used when we fallback from ai and give temporarily control to human bool temporary_human = false; do { // This flag can be set by derived classes (in overridden functions). player_type_changed_ = false; if (!skip_next_turn_) end_turn_ = false; statistics::reset_turn_stats(gamestate_.board_.teams()[player_number_ - 1].save_id()); if(current_team().is_human() || temporary_human) { LOG_NG << "is human...\n"; temporary_human = false; // If a side is dead end the turn, but play at least side=1's // turn in case all sides are dead if (gamestate_.board_.side_units(player_number_) != 0 || (resources::units->size() == 0 && player_number_ == 1)) { possible_end_play_signal signal = before_human_turn(); if (!signal) { signal = play_human_turn(); } if (signal) { switch (boost::apply_visitor(get_signal_type(), *signal)) { case END_LEVEL: return signal; case END_TURN: if (int(boost::apply_visitor(get_redo(),*signal)) == player_number_) { player_type_changed_ = true; // If new controller is not human, // reset gui to prev human one if (!gamestate_.board_.teams()[player_number_-1].is_human()) { browse_ = true; int s = find_human_team_before_current_player(); if (s <= 0) s = gui_->playing_side(); update_gui_to_player(s-1); } } } } } // Ending the turn commits all moves. undo_stack_->clear(); if ( !player_type_changed_ ) after_human_turn(); LOG_NG << "human finished turn...\n"; } else if(current_team().is_ai()) { try { play_ai_turn(); } catch(fallback_ai_to_human_exception&) { // Give control to a human for this turn. player_type_changed_ = true; temporary_human = true; } catch (end_level_exception & e) { //Don't know at the moment if these two are possible but can't hurt to add return possible_end_play_signal(e.to_struct()); } catch (end_turn_exception & e) { return possible_end_play_signal(e.to_struct()); } if(!player_type_changed_) { recorder.end_turn(); } } else if(current_team().is_network()) { PROPOGATE_END_PLAY_SIGNAL( play_network_turn() ); } else if(current_team().is_idle()) { end_turn_enable(false); do_idle_notification(); possible_end_play_signal signal = before_human_turn(); if (!signal) { signal = play_idle_loop(); } if (signal) { switch (boost::apply_visitor(get_signal_type(), *signal)) { case END_LEVEL: return signal; case END_TURN: LOG_NG << "Escaped from idle state with exception!" << std::endl; if (int(boost::apply_visitor(get_redo(), *signal)) == player_number_) { player_type_changed_ = true; // If new controller is not human, // reset gui to prev human one if (!gamestate_.board_.teams()[player_number_-1].is_human()) { browse_ = true; int s = find_human_team_before_current_player(); if (s <= 0) s = gui_->playing_side(); update_gui_to_player(s-1); } } } } } // Else current_team().is_empty(), so do nothing. } while (player_type_changed_); // Keep looping if the type of a team (human/ai/networked) // has changed mid-turn skip_next_turn_ = false; return boost::none; }
possible_end_play_signal playsingle_controller::play_turn() { whiteboard_manager_->on_gamestate_change(); gui_->new_turn(); gui_->invalidate_game_status(); events::raise_draw_event(); LOG_NG << "turn: " << turn() << "\n"; if(non_interactive()) { LOG_AIT << "Turn " << turn() << ":" << std::endl; } for (player_number_ = first_player_; player_number_ <= int(gamestate_.board_.teams().size()); ++player_number_) { // If a side is empty skip over it. if (current_team().is_empty()) continue; possible_end_play_signal signal; { save_blocker blocker; signal = init_side(); } if (signal) { switch (boost::apply_visitor(get_signal_type(), *signal)) { case END_TURN: if (current_team().is_network() == false) { turn_data_.send_data(); recorder.end_turn(); turn_data_.sync_network(); } continue; case END_LEVEL: return signal; } } if (replaying_) { LOG_NG << "doing replay " << player_number_ << "\n"; HANDLE_END_PLAY_SIGNAL ( replaying_ = ::do_replay() == REPLAY_FOUND_END_TURN ); LOG_NG << "result of replay: " << (replaying_?"true":"false") << "\n"; } else { ai_testing::log_turn_start(player_number_); PROPOGATE_END_PLAY_SIGNAL ( play_side() ); } finish_side_turn(); if(non_interactive()) { LOG_AIT << " Player " << player_number_ << ": " << current_team().villages().size() << " Villages" << std::endl; ai_testing::log_turn_end(player_number_); } HANDLE_END_PLAY_SIGNAL ( check_victory() ); //if loading a savegame, network turns might not have reset this yet loading_game_ = false; } //If the loop exits due to the last team having been processed, //player_number_ will be 1 too high if(player_number_ > static_cast<int>(gamestate_.board_.teams().size())) player_number_ = gamestate_.board_.teams().size(); finish_turn(); // Time has run out PROPOGATE_END_PLAY_SIGNAL ( check_time_over() ); return boost::none; }
possible_end_play_signal playmp_controller::play_human_turn(){ LOG_NG << "playmp::play_human_turn...\n"; remove_blindfold(); int cur_ticks = SDL_GetTicks(); show_turn_dialog(); if (!preferences::disable_auto_moves()) { HANDLE_END_PLAY_SIGNAL(execute_gotos()); } if (!linger_ || is_host()) { end_turn_enable(true); } while(!end_turn_) { turn_info_send send_safe(turn_data_); config cfg; if(network_reader_.read(cfg)) { turn_info::PROCESS_DATA_RESULT res; HANDLE_END_PLAY_SIGNAL( res = turn_data_.process_network_data(cfg, skip_replay_) ); //PROCESS_RESTART_TURN_TEMPORARY_LOCAL should be impossible because that's means the currently active side (that's us) left. if (res == turn_info::PROCESS_RESTART_TURN || res == turn_info::PROCESS_RESTART_TURN_TEMPORARY_LOCAL) { // Clean undo stack if turn has to be restarted (losing control) if ( undo_stack_->can_undo() ) { font::floating_label flabel(_("Undoing moves not yet transmitted to the server.")); SDL_Color color = {255,255,255,255}; flabel.set_color(color); SDL_Rect rect = gui_->map_area(); flabel.set_position(rect.w/2, rect.h/2); flabel.set_lifetime(150); flabel.set_clip_rect(rect); font::add_floating_label(flabel); } while( undo_stack_->can_undo() ) undo_stack_->undo(); end_turn_struct ets = {static_cast<unsigned>(gui_->playing_side())}; return possible_end_play_signal(ets); //throw end_turn_exception(gui_->playing_side()); } else if(res == turn_info::PROCESS_END_LINGER) { if(!linger_) replay::process_error("Received unexpected next_scenario durign the game"); else { //we end the turn immidiately to prevent receiving data of the next scenario while we are not playing it. end_turn(); } } } HANDLE_END_PLAY_SIGNAL( play_slice() ); HANDLE_END_PLAY_SIGNAL( check_end_level() ); if (!linger_ && (current_team().countdown_time() > 0) && saved_game_.mp_settings().mp_countdown) { SDL_Delay(1); const int ticks = SDL_GetTicks(); int new_time = current_team().countdown_time()-std::max<int>(1,(ticks - cur_ticks)); if (new_time > 0 ){ current_team().set_countdown_time(new_time); cur_ticks = ticks; if(current_team().is_human() && !beep_warning_time_) { beep_warning_time_ = new_time - WARNTIME + ticks; } if(counting_down()) { think_about_countdown(ticks); } } else { // Clock time ended // If no turn bonus or action bonus -> defeat const int action_increment = saved_game_.mp_settings().mp_countdown_action_bonus; if ( (saved_game_.mp_settings().mp_countdown_turn_bonus == 0 ) && (action_increment == 0 || current_team().action_bonus_count() == 0)) { // Not possible to end level in MP with throw end_level_exception(DEFEAT); // because remote players only notice network disconnection // Current solution end remaining turns automatically current_team().set_countdown_time(10); } return possible_end_play_signal(end_turn_exception().to_struct()); //throw end_turn_exception(); } } gui_->draw(); } return boost::none; }