void playmp_controller::play_network_turn(){ LOG_NG << "is networked...\n"; end_turn_enable(false); turn_data_.send_data(); while(end_turn_ != END_TURN_SYNCED && !is_regular_game_end() && !player_type_changed_) { if (!network_processing_stopped_) { process_network_data(); if (!mp_info_ || mp_info_->skip_replay_until_turn > 0) { skip_replay_ = false; } } play_slice_catch(); if (!network_processing_stopped_){ turn_data_.send_data(); } gui_->draw(); } LOG_NG << "finished networked...\n"; }
void playsingle_controller::play_ai_turn() { LOG_NG << "is ai...\n"; end_turn_enable(false); browse_ = true; gui_->recalculate_minimap(); const cursor::setter cursor_setter(cursor::WAIT); // Correct an oddball case where a human could have left delayed shroud // updates on before giving control to the AI. (The AI does not bother // with the undo stack, so it cannot delay shroud updates.) team & cur_team = current_team(); if ( !cur_team.auto_shroud_updates() ) { // We just took control, so the undo stack is empty. We still need // to record this change for the replay though. synced_context::run_in_synced_context("auto_shroud", replay_helper::get_auto_shroud(true)); } turn_data_.send_data(); turn_info_sync sync_safe(turn_data_); try { ai::manager::play_turn(player_number_); } catch (end_turn_exception&) { } gui_->recalculate_minimap(); gui_->invalidate_unit(); gui_->invalidate_game_status(); gui_->invalidate_all(); gui_->draw(); gui_->delay(100); }
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; }
void playsingle_controller::play_human_turn() { show_turn_dialog(); execute_gotos(); end_turn_enable(true); while(!end_turn_) { play_slice(); check_end_level(); gui_->draw(); } }
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; }
void playsingle_controller::linger() { LOG_NG << "beginning end-of-scenario linger\n"; browse_ = true; linger_ = true; // If we need to set the status depending on the completion state // the key to it is here. gui_->set_game_mode(game_display::LINGER_SP); // this is actually for after linger mode is over -- we don't // want to stay stuck in linger state when the *next* scenario // is over. set_completion setter(gamestate_,"running"); // change the end-turn button text to its alternate label gui_->get_theme().refresh_title2("button-endturn", "title2"); gui_->invalidate_theme(); gui_->redraw_everything(); // End all unit moves for (unit_map::iterator u = units_.begin(); u != units_.end(); ++u) { u->set_user_end_turn(true); } try { // Same logic as single-player human turn, but // *not* the same as multiplayer human turn. end_turn_enable(true); end_turn_ = false; while(!end_turn_) { // Reset the team number to make sure we're the right team. player_number_ = first_player_; play_slice(); gui_->draw(); } } catch(const game::load_game_exception &) { // Loading a new game is effectively a quit. if ( game::load_game_exception::game != "" ) { gamestate_ = game_state(); } throw; } // revert the end-turn button text to its normal label gui_->get_theme().refresh_title2("button-endturn", "title"); gui_->invalidate_theme(); gui_->redraw_everything(); gui_->set_game_mode(game_display::RUNNING); LOG_NG << "ending end-of-scenario linger\n"; }
void playsingle_controller::play_human_turn() { show_turn_dialog(); if (!preferences::disable_auto_moves()) { execute_gotos(); } end_turn_enable(true); while(!should_return_to_play_side()) { play_slice_catch(); gui_->draw(); } }
void playsingle_controller::play_ai_turn() { LOG_NG << "is ai...\n"; end_turn_enable(false); gui_->recalculate_minimap(); const cursor::setter cursor_setter(cursor::WAIT); // Correct an oddball case where a human could have left delayed shroud // updates on before giving control to the AI. (The AI does not bother // with the undo stack, so it cannot delay shroud updates.) team & cur_team = current_team(); if ( !cur_team.auto_shroud_updates() ) { // We just took control, so the undo stack is empty. We still need // to record this change for the replay though. synced_context::run_and_store("auto_shroud", replay_helper::get_auto_shroud(true)); } undo_stack().clear(); turn_data_.send_data(); try { try { if (!should_return_to_play_side()) { ai::manager::play_turn(current_side()); } } catch (return_to_play_side_exception&) { } catch (fallback_ai_to_human_exception&) { current_team().make_human(); player_type_changed_ = true; } } catch(...) { turn_data_.sync_network(); throw; } if(!should_return_to_play_side()) { end_turn_ = END_TURN_REQUIRED; } turn_data_.sync_network(); gui_->recalculate_minimap(); gui_->invalidate_unit(); gui_->invalidate_game_status(); gui_->invalidate_all(); gui_->draw(); }
void playsingle_controller::play_side_impl() { if (!skip_next_turn_) { end_turn_ = END_TURN_NONE; } if(replay_.get() != NULL) { REPLAY_RETURN res = replay_->play_side_impl(); if(res == REPLAY_FOUND_END_TURN) { end_turn_ = END_TURN_SYNCED; } if (player_type_changed_) { replay_.reset(); } } else if((current_team().is_local_human() && current_team().is_proxy_human())) { LOG_NG << "is human...\n"; // 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(current_side()) == 0 && !(gamestate().board_.units().size() == 0 && current_side() == 1)) { end_turn_ = END_TURN_REQUIRED; } before_human_turn(); if (end_turn_ == END_TURN_NONE) { play_human_turn(); } if ( !player_type_changed_ && !is_regular_game_end()) { after_human_turn(); } LOG_NG << "human finished turn...\n"; } else if(current_team().is_local_ai() || (current_team().is_local_human() && current_team().is_droid())) { play_ai_turn(); } else if(current_team().is_network()) { play_network_turn(); } else if(current_team().is_local_human() && current_team().is_idle()) { end_turn_enable(false); do_idle_notification(); before_human_turn(); if (end_turn_ == END_TURN_NONE) { play_idle_loop(); } } else { // we should have skipped over empty controllers before so this shouldn't be possible ERR_NG << "Found invalid side controller " << current_team().controller().to_string() << " (" << current_team().proxy_controller().to_string() << ") for side " << current_team().side() << "\n"; } }
void playmp_controller::play_linger_turn() { if (is_host()) { end_turn_enable(true); } while(end_turn_ == END_TURN_NONE) { config cfg; if(network_reader_.read(cfg)) { if(turn_data_.process_network_data(cfg) == turn_info::PROCESS_END_LINGER) { end_turn(); } } play_slice(); } }
void playmp_controller::handle_generic_event(const std::string& name){ turn_data_.send_data(); if (name == "ai_user_interact") { playsingle_controller::handle_generic_event(name); turn_data_.send_data(); } else if (name == "ai_gamestate_changed") { turn_data_.send_data(); } else if (name == "host_transfer"){ if (linger_){ end_turn_enable(true); gui_->invalidate_theme(); } } }
void playsingle_controller::linger() { LOG_NG << "beginning end-of-scenario linger\n"; linger_ = true; // If we need to set the status depending on the completion state // the key to it is here. gui_->set_game_mode(game_display::LINGER); // change the end-turn button text to its alternate label gui_->get_theme().refresh_title2("button-endturn", "title2"); gui_->invalidate_theme(); gui_->redraw_everything(); // End all unit moves gamestate().board_.set_all_units_user_end_turn(); try { // Same logic as single-player human turn, but // *not* the same as multiplayer human turn. end_turn_enable(true); end_turn_ = END_TURN_NONE; while(end_turn_ == END_TURN_NONE) { play_slice(); gui_->draw(); } } catch(const game::load_game_exception &) { // Loading a new game is effectively a quit. if ( game::load_game_exception::game != "" ) { saved_game_ = saved_game(); } throw; } // revert the end-turn button text to its normal label gui_->get_theme().refresh_title2("button-endturn", "title"); gui_->invalidate_theme(); gui_->redraw_everything(); gui_->set_game_mode(game_display::RUNNING); LOG_NG << "ending end-of-scenario linger\n"; }
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; }
void playmp_controller::play_human_turn() { LOG_NG << "playmp::play_human_turn...\n"; assert(!linger_); remove_blindfold(); boost::scoped_ptr<countdown_clock> timer; if(saved_game_.mp_settings().mp_countdown) { timer.reset(new countdown_clock(current_team())); } show_turn_dialog(); if(undo_stack_->can_undo()) { // If we reload a networked mp game we cannot undo moves made before the save // Becasue other players already received them if(!current_team().auto_shroud_updates()) { synced_context::run_and_store("update_shroud", replay_helper::get_update_shroud()); } undo_stack_->clear(); } if (!preferences::disable_auto_moves()) { execute_gotos(); } end_turn_enable(true); while(!should_return_to_play_side()) { try { process_network_data(); if (player_type_changed_) { // 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(); } check_objectives(); play_slice_catch(); if(timer) { bool time_left = timer->update(); if(!time_left) { end_turn_ = END_TURN_REQUIRED; } } } catch(...) { turn_data_.send_data(); throw; } turn_data_.send_data(); gui_->draw(); } }
void playmp_controller::play_human_turn(){ LOG_NG << "playmp::play_human_turn...\n"; command_disabled_resetter reset_commands; int cur_ticks = SDL_GetTicks(); show_turn_dialog(); execute_gotos(); if (!linger_ || is_host_) { end_turn_enable(true); } while(!end_turn_) { try { config cfg; const network::connection res = network::receive_data(cfg); std::deque<config> backlog; if(res != network::null_connection) { if (turn_data_->process_network_data(cfg, res, backlog, skip_replay_) == turn_info::PROCESS_RESTART_TURN) { // 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(); throw end_turn_exception(gui_->playing_side()); } } play_slice(); check_end_level(); // give a chance to the whiteboard to continue an execute_all_actions resources::whiteboard->continue_execute_all(); } catch(const end_level_exception&) { turn_data_->send_data(); throw; } if (!linger_ && (current_team().countdown_time() > 0) && gamestate_.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 = gamestate_.mp_settings().mp_countdown_action_bonus; if ( (gamestate_.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); } else { const int maxtime = gamestate_.mp_settings().mp_countdown_reservoir_time; int secs = gamestate_.mp_settings().mp_countdown_turn_bonus; secs += action_increment * current_team().action_bonus_count(); current_team().set_action_bonus_count(0); secs = (secs > maxtime) ? maxtime : secs; current_team().set_countdown_time(1000 * secs); } turn_data_->send_data(); if (!rand_rng::has_new_seed_callback()) { throw end_turn_exception(); } } } gui_->draw(); turn_data_->send_data(); } }
void playsingle_controller::play_side(const unsigned int side_number, bool save) { //check for team-specific items in the scenario gui_->parse_team_overlays(); //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(teams_[side_number - 1].save_id()); if(current_team().is_human() || temporary_human) { LOG_NG << "is human...\n"; temporary_human = false; try{ before_human_turn(save); play_human_turn(); } catch(end_turn_exception& end_turn) { if (end_turn.redo == side_number) { player_type_changed_ = true; // If new controller is not human, // reset gui to prev human one if (!teams_[side_number-1].is_human()) { browse_ = true; int s = find_human_team_before(side_number); 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; } } else if(current_team().is_network()) { play_network_turn(); } else if(current_team().is_idle()) { try{ end_turn_enable(false); do_idle_notification(); before_human_turn(save); play_idle_loop(); } catch(end_turn_exception& end_turn) { LOG_NG << "Escaped from idle state with exception!" << std::endl; if (end_turn.redo == side_number) { player_type_changed_ = true; // If new controller is not human, // reset gui to prev human one if (!teams_[side_number-1].is_human()) { browse_ = true; int s = find_human_team_before(side_number); 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; }
void playmp_controller::play_human_turn() { LOG_NG << "playmp::play_human_turn...\n"; assert(!linger_); assert(gamestate_->init_side_done()); assert(gamestate().gamedata_.phase() == game_data::PLAY); mp_ui_alerts::turn_changed(current_team().current_player()); LOG_NG << "events::commands_disabled=" << events::commands_disabled <<"\n"; remove_blindfold(); const std::unique_ptr<countdown_clock> timer(saved_game_.mp_settings().mp_countdown ? new countdown_clock(current_team()) : nullptr); show_turn_dialog(); if(undo_stack().can_undo()) { // If we reload a networked mp game we cannot undo moves made before the save // because other players already received them if(!current_team().auto_shroud_updates()) { synced_context::run_and_store("update_shroud", replay_helper::get_update_shroud()); } undo_stack().clear(); } if (!preferences::disable_auto_moves()) { execute_gotos(); } end_turn_enable(true); while(!should_return_to_play_side()) { try { process_network_data(); check_objectives(); play_slice_catch(); if (player_type_changed_) { // 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.")); color_t color {255,255,255,SDL_ALPHA_OPAQUE}; 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(); } if(timer) { bool time_left = timer->update(); if(!time_left) { end_turn_ = END_TURN_REQUIRED; } } } catch(...) { turn_data_.send_data(); throw; } turn_data_.send_data(); } }
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; }
void playsingle_controller::play_side() { //check for team-specific items in the scenario gui_->parse_team_overlays(); maybe_do_init_side(); if(is_regular_game_end()) { return; } //flag used when we fallback from ai and give temporarily control to human bool temporary_human = false; do { //Update viewing team in case it has changed during the loop. if(int side_num = play_controller::find_last_visible_team()) { if(side_num != this->gui_->viewing_side()) { update_gui_to_player(side_num - 1); } } // This flag can be set by derived classes (in overridden functions). player_type_changed_ = false; if (!skip_next_turn_) end_turn_ = END_TURN_NONE; statistics::reset_turn_stats(gamestate_.board_.teams()[player_number_ - 1].save_id()); if((current_team().is_local_human() && current_team().is_proxy_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 && !(gamestate_.board_.units().size() == 0 && player_number_ == 1)) { end_turn_ = END_TURN_REQUIRED; } before_human_turn(); if (end_turn_ == END_TURN_NONE) { play_human_turn(); } if(is_regular_game_end()) { return; } if ( !player_type_changed_ ) { after_human_turn(); } LOG_NG << "human finished turn...\n"; } else if(current_team().is_local_ai() || (current_team().is_local_human() && current_team().is_droid())) { 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; } if(is_regular_game_end()) { return; } } else if(current_team().is_network()) { play_network_turn(); if(is_regular_game_end()) { return; } } else if(current_team().is_local_human() && current_team().is_idle()) { end_turn_enable(false); do_idle_notification(); before_human_turn(); if (end_turn_ == END_TURN_NONE) { play_idle_loop(); if(is_regular_game_end()) { return; } } } else { assert(current_team().is_empty()); // Do nothing. } } while (player_type_changed_); // Keep looping if the type of a team (human/ai/networked) // has changed mid-turn sync_end_turn(); assert(end_turn_ == END_TURN_SYNCED); skip_next_turn_ = false; }