示例#1
0
void play_controller::play_side()
{
    //check for team-specific items in the scenario
    gui_->parse_team_overlays();
    do {
        update_viewing_player();
        {
            save_blocker blocker;
            maybe_do_init_side();
            if(is_regular_game_end()) {
                return;
            }
        }
        // This flag can be set by derived classes (in overridden functions).
        player_type_changed_ = false;

        statistics::reset_turn_stats(gamestate().board_.teams()[current_side() - 1].save_id());

        play_side_impl();

        if(is_regular_game_end()) {
            return;
        }

    } while (player_type_changed_);
    // Keep looping if the type of a team (human/ai/networked)
    // has changed mid-turn
    sync_end_turn();
}
示例#2
0
void play_controller::check_victory()
{
    if(linger_)
    {
        return;
    }

    if (is_regular_game_end()) {
        return;
    }
    bool continue_level, found_player, found_network_player, invalidate_all;
    std::set<unsigned> not_defeated;

    gamestate().board_.check_victory(continue_level, found_player, found_network_player, invalidate_all, not_defeated, remove_from_carryover_on_defeat_);

    if (invalidate_all) {
        gui_->invalidate_all();
    }

    if (continue_level) {
        return ;
    }

    if (found_player || found_network_player) {
        pump().fire("enemies_defeated");
        if (is_regular_game_end()) {
            return;
        }
    }

    DBG_EE << "victory_when_enemies_defeated: " << victory_when_enemies_defeated_ << std::endl;
    DBG_EE << "found_player: " << found_player << std::endl;
    DBG_EE << "found_network_player: " << found_network_player << std::endl;

    if (!victory_when_enemies_defeated_ && (found_player || found_network_player)) {
        // This level has asked not to be ended by this condition.
        return;
    }

    if (gui_->video().non_interactive()) {
        LOG_AIT << "winner: ";
        for (unsigned l : not_defeated) {
            std::string ai = ai::manager::get_active_ai_identifier_for_side(l);
            if (ai.empty()) ai = "default ai";
            LOG_AIT << l << " (using " << ai << ") ";
        }
        LOG_AIT << std::endl;
        ai_testing::log_victory(not_defeated);
    }

    DBG_EE << "throwing end level exception..." << std::endl;
    //Also proceed to the next scenario when another player survived.
    end_level_data el_data;
    el_data.proceed_to_next_level = found_player || found_network_player;
    el_data.is_victory = found_player;
    set_end_level_data(el_data);
}
void 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_ <= int(gamestate_.board_.teams().size()); ++player_number_)
	{
		// If a side is empty skip over it.
		if (current_team().is_empty()) continue;

		{
			save_blocker blocker;
			init_side_begin(false);
			if(init_side_done_) {
				//This is the case in a reloaded game where teh side was initilizes before saving the game.
				init_side_end();
			}
		}

		ai_testing::log_turn_start(player_number_);
		play_side();
		if(is_regular_game_end()) {
			return;
		}
		finish_side_turn();
		if(is_regular_game_end()) {
			return;
		}
		if(non_interactive()) {
			LOG_AIT << " Player " << player_number_ << ": " <<
				current_team().villages().size() << " Villages" <<
				std::endl;
			ai_testing::log_turn_end(player_number_);
		}
	}
	//If the loop exits due to the last team having been processed,
	//player_number_ will be 1 too high
	//TODO: Why else could the loop exit?
	if(player_number_ > static_cast<int>(gamestate_.board_.teams().size()))
		player_number_ = gamestate_.board_.teams().size();

	finish_turn();

	// Time has run out
	check_time_over();
}
示例#4
0
void play_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(gui_->video().non_interactive()) {
        LOG_AIT << "Turn " << turn() << ":" << std::endl;
    }

    for (; gamestate_->player_number_ <= int(gamestate().board_.teams().size()); ++gamestate_->player_number_)
    {
        // If a side is empty skip over it.
        if (current_team().is_empty()) {
            continue;
        }
        init_side_begin();
        if(gamestate_->init_side_done()) {
            // This is the case in a reloaded game where the side was initialized before saving the game.
            init_side_end();
        }

        ai_testing::log_turn_start(current_side());
        play_side();
        if(is_regular_game_end()) {
            return;
        }
        finish_side_turn();
        if(is_regular_game_end()) {
            return;
        }
        if(gui_->video().non_interactive()) {
            LOG_AIT << " Player " << current_side() << ": " <<
                    current_team().villages().size() << " Villages" <<
                    std::endl;
            ai_testing::log_turn_end(current_side());
        }
    }
    // If the loop exits due to the last team having been processed.
    gamestate_->player_number_ = gamestate().board_.teams().size();

    finish_turn();

    // Time has run out
    check_time_over();
}
示例#5
0
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 playmp_controller::process_network_data(bool chat_only)
{
	if(end_turn_ == END_TURN_SYNCED || is_regular_game_end() || player_type_changed_) {
		return;
	}
	turn_info::PROCESS_DATA_RESULT res = turn_info::PROCESS_CONTINUE;
	config cfg;
	if(!resources::recorder->at_end()) {
		res = turn_info::replay_to_process_data_result(do_replay());
	}
	else if(network_reader_.read(cfg)) {
		res = turn_data_.process_network_data(cfg, chat_only);
	}

	if (res == turn_info::PROCESS_CANNOT_HANDLE) {
		network_reader_.push_front(std::move(cfg));
	}
	else if (res == turn_info::PROCESS_RESTART_TURN) {
		player_type_changed_ = true;
	}
	else if (res == turn_info::PROCESS_END_TURN) {
		end_turn_ = END_TURN_SYNCED;
	}
	else if (res == turn_info::PROCESS_END_LEVEL) {
	}
	else if (res == turn_info::PROCESS_END_LINGER) {
		replay::process_error("Received unexpected next_scenario during the game");
	}
}
void playsingle_controller::check_time_over(){
	bool time_left = gamestate_.tod_manager_.next_turn(gamestate_.gamedata_);
	it_is_a_new_turn_ = true;
	if(!time_left) {
		LOG_NG << "firing time over event...\n";
		set_scontext_synced_base sync;
		pump().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;
		}

		if(non_interactive()) {
			LOG_AIT << "time over (draw)\n";
			ai_testing::log_draw();
		}

		check_victory();
		if (is_regular_game_end()) {
			return;
		}
		end_level_data e;
		e.proceed_to_next_level = false;
		e.is_victory = false;
		set_end_level_data(e);
	}
}
void playsingle_controller::play_scenario_main_loop()
{
	LOG_NG << "starting main loop\n" << (SDL_GetTicks() - ticks()) << "\n";


	// Avoid autosaving after loading, but still
	// allow the first turn to have an autosave.
	ai_testing::log_game_start();
	if(gamestate().board_.teams().empty())
	{
		ERR_NG << "Playing game with 0 teams." << std::endl;
	}
	while(true) {
		try {
			play_turn();
			if (is_regular_game_end()) {
				turn_data_.send_data();
				return;
			}
			gamestate_->player_number_ = 1;
		}
		catch(const reset_gamestate_exception& ex) {
			//
			// TODO:
			//
			// The MP replay feature still doesn't work properly (causes OOS)
			// because:
			//
			// 1) The undo stack is not reset along with the gamestate (fixed).
			// 2) The server_request_number_ is not reset along with the
			//    gamestate (fixed).
			// 3) chat and other unsynced actions are inserted in the middle of
			//    the replay bringing the replay_pos in unorder (fixed).
			// 4) untracked changes in side controllers are lost when resetting
			//    gamestate (fixed).
			// 5) The game should have a stricter check for whether the loaded
			//    game is actually a parent of this game.
			// 6) If an action was undone after a game was saved it can cause
			//    OOS if the undone action is in the snapshot of the saved
			//    game (luckily this is never the case for autosaves).
			//
			boost::dynamic_bitset<> local_players;
			local_players.resize(gamestate().board_.teams().size(), true);
			//Preserve side controllers, becasue we won't get the side controoller updates again when replaying.
			for(size_t i = 0; i < local_players.size(); ++i) {
				local_players[i] = gamestate().board_.teams()[i].is_local();
			}
			reset_gamestate(*ex.level, (*ex.level)["replay_pos"]);
			for(size_t i = 0; i < local_players.size(); ++i) {
				resources::gameboard->teams()[i].set_local(local_players[i]);
			}
			play_scenario_init();
			replay_.reset(new replay_controller(*this, false, ex.level));
			if(ex.start_replay) {
				replay_->play_replay();
			}
		}
	} //end for loop
}
void playsingle_controller::maybe_linger()
{
	// mouse_handler expects at least one team for linger mode to work.
	assert(is_regular_game_end());
	if (get_end_level_data_const().transient.linger_mode && !gamestate().board_.teams().empty()) {
		linger();
	}
}
示例#10
0
void play_controller::start_game()
{
    fire_preload();

    if(!gamestate().start_event_fired_)
    {
        gamestate().start_event_fired_ = true;
        resources::recorder->add_start_if_not_there_yet();
        resources::recorder->get_next_action();

        set_scontext_synced sync;

        fire_prestart();
        if (is_regular_game_end()) {
            return;
        }

        for ( int side = gamestate().board_.teams().size(); side != 0; --side )
            actions::clear_shroud(side, false, false);

        init_gui();
        LOG_NG << "first_time..." << (is_skipping_replay() ? "skipping" : "no skip") << "\n";

        events::raise_draw_event();
        fire_start();
        if (is_regular_game_end()) {
            return;
        }
        sync.do_final_checkup();
        gui_->recalculate_minimap();
        // Initialize countdown clock.
        for (const team& t : gamestate().board_.teams())
        {
            if (saved_game_.mp_settings().mp_countdown) {
                t.set_countdown_time(1000 * saved_game_.mp_settings().mp_countdown_init_time);
            }
        }
    }
    else
    {
        init_gui();
        events::raise_draw_event();
        gamestate().gamedata_.set_phase(game_data::PLAY);
        gui_->recalculate_minimap();
    }
}
示例#11
0
void playsingle_controller::check_objectives()
{
	const team &t = gamestate().board_.teams()[gui_->viewing_team()];

	if (!is_regular_game_end() && !is_browsing() && t.objectives_changed()) {
		dialogs::show_objectives(get_scenario_name().str(), t.objectives());
		t.reset_objectives_changed();
	}
}
示例#12
0
void playsingle_controller::check_objectives()
{
	if (!gamestate().board_.teams().empty()) {
		const team &t = gamestate().board_.teams()[gui_->viewing_team()];

		if (!is_regular_game_end() && !is_browsing() && t.objectives_changed()) {
			show_objectives();
		}
	}
}
void playsingle_controller::before_human_turn()
{
	log_scope("player turn");
	assert(!linger_);
	if(end_turn_ != END_TURN_NONE) {
		return;
	}
	//TODO: why do we need the next line?
	ai::manager::raise_turn_started();

	if(init_side_done_now_ && !is_regular_game_end()) {
		update_savegame_snapshot();
		savegame::autosave_savegame save(saved_game_, *gui_, preferences::save_compression_format());
		save.autosave(game_config::disable_autosave, preferences::autosavemax(), preferences::INFINITE_AUTO_SAVES);
	}

	if(preferences::turn_bell() && !is_regular_game_end()) {
		sound::play_bell(game_config::sounds::turn_bell);
	}
}
示例#14
0
void playsingle_controller::on_replay_end(bool is_unit_test)
{
	if(is_unit_test) {
		replay_->return_to_play_side();
		if(!is_regular_game_end()) {
			end_level_data e;
			e.proceed_to_next_level = false;
			e.is_victory = false;
			set_end_level_data(e);
		}
	}
}
示例#15
0
bool playsingle_controller::should_return_to_play_side()
{
	if(player_type_changed_ || is_regular_game_end()) {
		return true;
	}
	else if (end_turn_ == END_TURN_NONE || replay_.get() != 0 || current_team().is_network()) {
		return false;
	}
	else {
		return true;
	}
}
示例#16
0
std::string playsingle_controller::describe_result() const
{
	if(!is_regular_game_end()) {
		return "NONE";
	}
	else if(get_end_level_data_const().is_victory){
		return "VICTORY";
	}
	else {
		return "DEFEAT";
	}
}
示例#17
0
void playsingle_controller::show_turn_dialog(){
	if(preferences::turn_dialog() && !is_regular_game_end() ) {
		blindfold b(*gui_, true); //apply a blindfold for the duration of this dialog
		gui_->redraw_everything();
		gui_->recalculate_minimap();
		std::string message = _("It is now $name|’s turn");
		utils::string_map symbols;
		symbols["name"] = gamestate().board_.teams()[current_side() - 1].side_name();
		message = utils::interpolate_variables_into_string(message, &symbols);
		gui2::show_transient_message(gui_->video(), "", message);
	}
}
示例#18
0
void playmp_controller::maybe_linger()
{
	// mouse_handler expects at least one team for linger mode to work.
	assert(is_regular_game_end());
	if (!get_end_level_data_const().transient.linger_mode || gamestate().board_.teams().empty()) {
		if(!is_host()) {
			// If we continue without lingering we need to
			// make sure the host uploads the next scenario
			// before we attempt to download it.
			wait_for_upload();
		}
	} else {
		linger();
	}
}
示例#19
0
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";
	}
}
示例#20
0
void playsingle_controller::before_human_turn()
{
	log_scope("player turn");
	assert(!linger_);
	if(end_turn_ != END_TURN_NONE || is_regular_game_end()) {
		return;
	}

	if(init_side_done_now_) {
		scoped_savegame_snapshot snapshot(*this);
		savegame::autosave_savegame save(saved_game_, *gui_, preferences::save_compression_format());
		save.autosave(game_config::disable_autosave, preferences::autosavemax(), preferences::INFINITE_AUTO_SAVES);
	}

	if(preferences::turn_bell()) {
		sound::play_bell(game_config::sounds::turn_bell);
	}
}
void playsingle_controller::play_scenario_main_loop() {
	LOG_NG << "starting main loop\n" << (SDL_GetTicks() - ticks_) << "\n";


	// Avoid autosaving after loading, but still
	// allow the first turn to have an autosave.
	ai_testing::log_game_start();
	if(gamestate_.board_.teams().empty())
	{
		ERR_NG << "Playing game with 0 teams." << std::endl;
	}
	while(true) {
		play_turn();
		if (is_regular_game_end()) {
			return;
		}
		player_number_ = 1;
	} //end for loop
}
示例#22
0
void playmp_controller::linger()
{
	LOG_NG << "beginning end-of-scenario linger\n";
	linger_ = true;
	// If we need to set the status depending on the completion state
	// we're needed here.
	gui_->set_game_mode(game_display::LINGER);
	// End all unit moves
	gamestate().board_.set_all_units_user_end_turn();

	set_end_scenario_button();
	assert(is_regular_game_end());
	if ( get_end_level_data_const().transient.reveal_map ) {
		// Change the view of all players and observers
		// to see the whole map regardless of shroud and fog.
		update_gui_to_player(gui_->viewing_team(), true);
	}
	bool quit;
	do {
		quit = true;
		try {
			// reimplement parts of play_side()
			turn_data_.send_data();
			end_turn_ = END_TURN_NONE;
			play_linger_turn();
			after_human_turn();
			LOG_NG << "finished human turn" << std::endl;
		} catch (const savegame::load_game_exception&) {
			LOG_NG << "caught load-game-exception" << std::endl;
			// this should not happen, the option to load a game is disabled
			throw;
		} catch (const ingame_wesnothd_error&) {
			LOG_NG << "caught network-error-exception" << std::endl;
			quit = false;
		}
	} while (!quit);

	reset_end_scenario_button();

	LOG_NG << "ending end-of-scenario linger\n";
}
示例#23
0
void playsingle_controller::play_scenario_main_loop()
{
	LOG_NG << "starting main loop\n" << (SDL_GetTicks() - ticks()) << "\n";


	// Avoid autosaving after loading, but still
	// allow the first turn to have an autosave.
	ai_testing::log_game_start();
	if(gamestate().board_.teams().empty())
	{
		ERR_NG << "Playing game with 0 teams." << std::endl;
	}
	while(true) {
		try {
			play_turn();
			if (is_regular_game_end()) {
				return;
			}
			gamestate_->player_number_ = 1;
		}
		catch(const reset_gamestate_exception& ex) {
			/**
				@TODO: The mp replay feature still doesnt work properly (casues OOS) becasue:
					1) The undo stack is not reset along with the gamestate (fixed).
					2) The server_request_number_ is not reset along with the gamestate (fixed).
					3) chat and other unsynced actions are inserted in the middle of the replay bringing the replay_pos in unorder (fixed).
					4) untracked changes in side controllers are lost when resetting gamestate.
					5) The game should have a stricter check for whether the loaded game is actually a parent of this game.
					6) If an action was undone after a game was saved it can casue if teh undone action is in the snapshot of the saved game. (luckyli this is never the case for autosaves)
			*/
			reset_gamestate(*ex.level, (*ex.level)["replay_pos"]);
			play_scenario_init(*ex.level);
			mp_replay_.reset(new replay_controller(*this, false, ex.level));
			mp_replay_->play_replay();
		}
	} //end for loop
}
	virtual bool should_return_to_play_side()
	{ return is_regular_game_end(); }
示例#25
0
	virtual bool should_return_to_play_side()
	{ return player_type_changed_ || end_turn_ != END_TURN_NONE || is_regular_game_end(); }
示例#26
0
LEVEL_RESULT playsingle_controller::play_scenario(const config& level)
{
	LOG_NG << "in playsingle_controller::play_scenario()...\n";

	// Start music.
	BOOST_FOREACH(const config &m, level.child_range("music")) {
		sound::play_music_config(m);
	}
	sound::commit_music_changes();

	if(!this->is_skipping_replay()) {
		show_story(gui_->video(), get_scenario_name(), level.child_range("story"));
	}
	gui_->labels().read(level);

	// Read sound sources
	assert(soundsources_manager_ != NULL);
	BOOST_FOREACH(const config &s, level.child_range("sound_source")) {
		try {
			soundsource::sourcespec spec(s);
			soundsources_manager_->add(spec);
		} catch (bad_lexical_cast &) {
			ERR_NG << "Error when parsing sound_source config: bad lexical cast." << std::endl;
			ERR_NG << "sound_source config was: " << s.debug() << std::endl;
			ERR_NG << "Skipping this sound source..." << std::endl;
		}
	}
	LOG_NG << "entering try... " << (SDL_GetTicks() - ticks()) << "\n";
	try {
		play_scenario_init();
		// clears level config;
		this->saved_game_.remove_snapshot();

		if (!is_regular_game_end() && !linger_) {
			play_scenario_main_loop();
		}
		if (game_config::exit_at_end) {
			exit(0);
		}
		const bool is_victory = get_end_level_data_const().is_victory;

		if(gamestate().gamedata_.phase() <= game_data::PRESTART) {
			sdl::draw_solid_tinted_rectangle(
				0, 0, gui_->video().getx(), gui_->video().gety(), 0, 0, 0, 1.0,
				gui_->video().getSurface()
				);
			update_rect(0, 0, gui_->video().getx(), gui_->video().gety());
		}

		ai_testing::log_game_end();

		const end_level_data& end_level = get_end_level_data_const();
		if (!end_level.transient.custom_endlevel_music.empty()) {
			if (!is_victory) {
				set_defeat_music_list(end_level.transient.custom_endlevel_music);
			} else {
				set_victory_music_list(end_level.transient.custom_endlevel_music);
			}
		}

		if (gamestate().board_.teams().empty())
		{
			//store persistent teams
			saved_game_.set_snapshot(config());

			return LEVEL_RESULT::VICTORY; // this is probably only a story scenario, i.e. has its endlevel in the prestart event
		}
		if(linger_) {
			LOG_NG << "resuming from loaded linger state...\n";
			//as carryover information is stored in the snapshot, we have to re-store it after loading a linger state
			saved_game_.set_snapshot(config());
			if(!is_observer()) {
				persist_.end_transaction();
			}
			return LEVEL_RESULT::VICTORY;
		}
		pump().fire(is_victory ? "victory" : "defeat");
		{ // Block for set_scontext_synced_base
			set_scontext_synced_base sync;
			pump().fire("scenario end");
		}
		if(end_level.proceed_to_next_level) {
			gamestate().board_.heal_all_survivors();
		}
		if(is_observer()) {
			gui2::show_transient_message(gui_->video(), _("Game Over"), _("The game is over."));
			return LEVEL_RESULT::OBSERVER_END;
		}
		// If we're a player, and the result is victory/defeat, then send
		// a message to notify the server of the reason for the game ending.
		network::send_data(config_of
			("info", config_of
				("type", "termination")
				("condition", "game over")
				("result", is_victory ? "victory" : "defeat")
			));
		// Play victory music once all victory events
		// are finished, if we aren't observers.
		//
		// Some scenario authors may use 'continue'
		// result for something that is not story-wise
		// a victory, so let them use [music] tags
		// instead should they want special music.
		const std::string& end_music = is_victory ? select_victory_music() : select_defeat_music();
		if(end_music.empty() != true) {
			sound::play_music_once(end_music);
		}
		persist_.end_transaction();
		return is_victory ? LEVEL_RESULT::VICTORY : LEVEL_RESULT::DEFEAT;
	} 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;
	} catch(network::error& e) {
		bool disconnect = false;
		if(e.socket) {
			e.disconnect();
			disconnect = true;
		}

		scoped_savegame_snapshot snapshot(*this);
		savegame::ingame_savegame save(saved_game_, *gui_, preferences::save_compression_format());
		save.save_game_interactive(gui_->video(), _("A network disconnection has occurred, and the game cannot continue. Do you want to save the game?"), gui::YES_NO);
		if(disconnect) {
			throw network::error();
		} else {
			return LEVEL_RESULT::QUIT;
		}
	}

	return LEVEL_RESULT::QUIT;
}
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;
}