possible_end_play_signal playsingle_controller::play_scenario_main_loop(end_level_data & end_level, bool & /*past_prestart*/) { LOG_NG << "starting main loop\n" << (SDL_GetTicks() - ticks_) << "\n"; // Initialize countdown clock. std::vector<team>::const_iterator t; for(t = gamestate_.board_.teams().begin(); t != gamestate_.board_.teams().end(); ++t) { if (saved_game_.mp_settings().mp_countdown && !loading_game_ ) { t->set_countdown_time(1000 * saved_game_.mp_settings().mp_countdown_init_time); } } // if we loaded a save file in linger mode, skip to it. if (linger_) { //determine the bonus gold handling for this scenario end_level.read(level_.child_or_empty("end_level_data")); end_level.transient.carryover_report = false; end_level.transient.disabled = true; end_level_struct els = { SKIP_TO_LINGER }; return possible_end_play_signal ( els ); //throw end_level_exception(SKIP_TO_LINGER); } // Avoid autosaving after loading, but still // allow the first turn to have an autosave. do_autosaves_ = !loading_game_; ai_testing::log_game_start(); for(; ; first_player_ = 1) { PROPOGATE_END_PLAY_SIGNAL( play_turn() ); do_autosaves_ = true; } //end for loop 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 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 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; }
//make only one side move possible_end_play_signal replay_controller::play_side() { DBG_REPLAY << "Status turn number: " << turn() << "\n"; DBG_REPLAY << "Replay_Controller turn number: " << current_turn_ << "\n"; DBG_REPLAY << "Player number: " << player_number_ << "\n"; // If a side is empty skip over it. if (!current_team().is_empty()) { statistics::reset_turn_stats(current_team().save_id()); possible_end_play_signal signal = play_controller::init_side(true); if (signal) { switch (boost::apply_visitor(get_signal_type(), *signal) ) { case END_TURN: return signal; case END_LEVEL: //VICTORY/DEFEAT end_level_exception shall not return to title screen LEVEL_RESULT res = boost::apply_visitor(get_result(), *signal); if ( res != VICTORY && res != DEFEAT ) return signal; } } DBG_REPLAY << "doing replay " << player_number_ << "\n"; // if have reached the end we don't want to execute finish_side_turn and finish_turn // becasue we might not have enough data to execute them (like advancements during turn_end for example) try { if(do_replay() != REPLAY_FOUND_END_TURN) { // We reached the end of teh replay without finding and end turn tag. return boost::none; } } catch(end_level_exception& e){ //VICTORY/DEFEAT end_level_exception shall not return to title screen if (e.result != VICTORY && e.result != DEFEAT) { return possible_end_play_signal(e.to_struct()); } } catch (end_turn_exception & e) { return possible_end_play_signal(e.to_struct()); } finish_side_turn(); } player_number_++; if (static_cast<size_t>(player_number_) > gamestate_.board_.teams().size()) { //during the orginal game player_number_ would also be gamestate_.board_.teams().size(), player_number_ = gamestate_.board_.teams().size(); finish_turn(); gamestate_.tod_manager_.next_turn(*resources::gamedata); it_is_a_new_turn_ = true; player_number_ = 1; current_turn_++; gui_->new_turn(); } // This is necessary for replays in order to show possible movements. gamestate_.board_.new_turn(player_number_); update_teams(); update_gui(); 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; }