void playmp_controller::play_human_turn(){
	LOG_NG << "playmp::play_human_turn...\n";

	remove_blindfold();

	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);
				}
				turn_data_->send_data();

				if (!rand_rng::has_new_seed_callback()) {
					throw end_turn_exception();
				}
			}
		}

		gui_->draw();

		turn_data_->send_data();
	}
}
Esempio n. 2
0
std::vector<target> default_ai_context_impl::find_targets(const move_map& enemy_dstsrc)
{

	log_scope2(log_ai, "finding targets...");
	unit_map &units_ = *resources::units;
	unit_map::iterator leader = units_.find_leader(get_side());
	gamemap &map_ = *resources::game_map;
	std::vector<team> teams_ = *resources::teams;
	const bool has_leader = leader != units_.end();

	std::vector<target> targets;

	//=== start getting targets

	//if enemy units are in range of the leader, then we target the enemies who are in range.
	if(has_leader) {
		double threat = power_projection(leader->get_location(), enemy_dstsrc);
		if(threat > 0.0) {
			//find the location of enemy threats
			std::set<map_location> threats;

			map_location adj[6];
			get_adjacent_tiles(leader->get_location(), adj);
			for(size_t n = 0; n != 6; ++n) {
				std::pair<move_map::const_iterator,move_map::const_iterator> itors = enemy_dstsrc.equal_range(adj[n]);
				while(itors.first != itors.second) {
					if(units_.count(itors.first->second)) {
						threats.insert(itors.first->second);
					}

					++itors.first;
				}
			}

			assert(threats.empty() == false);

#ifdef SUOKKO
			//FIXME: suokko's revision 29531 included this change.  Correct?
			const double value = threat*get_protect_leader()/leader->second.hitpoints();
#else
			const double value = threat/double(threats.size());
#endif
			for(std::set<map_location>::const_iterator i = threats.begin(); i != threats.end(); ++i) {
				LOG_AI << "found threat target... " << *i << " with value: " << value << "\n";
				targets.push_back(target(*i,value,target::THREAT));
			}
		}
	}

	double corner_distance = distance_between(map_location(0,0), map_location(map_.w(),map_.h()));
	double village_value = get_village_value();
	if(has_leader && village_value > 0.0) {
		std::map<map_location,pathfind::paths> friends_possible_moves;
		move_map friends_srcdst, friends_dstsrc;
		calculate_possible_moves(friends_possible_moves, friends_srcdst, friends_dstsrc, false, true);

		const std::vector<map_location>& villages = map_.villages();
		for(std::vector<map_location>::const_iterator t =
				villages.begin(); t != villages.end(); ++t) {

			assert(map_.on_board(*t));
			bool ally_village = false;
			for (size_t i = 0; i != teams_.size(); ++i)
			{
				if (!current_team().is_enemy(i + 1) && teams_[i].owns_village(*t)) {
					ally_village = true;
					break;
				}
			}

			if (ally_village)
			{
				//Support seems to cause the AI to just 'sit around' a lot, so
				//only turn it on if it's explicitly enabled.
				if(get_support_villages()) {
					double enemy = power_projection(*t, enemy_dstsrc);
					if (enemy > 0)
					{
						enemy *= 1.7;
						double our = power_projection(*t, friends_dstsrc);
						double value = village_value * our / enemy;
						add_target(target(*t, value, target::SUPPORT));
					}
				}
			}
			else
			{
				double leader_distance = distance_between(*t, leader->get_location());
				double value = village_value * (1.0 - leader_distance / corner_distance);
				LOG_AI << "found village target... " << *t
					<< " with value: " << value
					<< " distance: " << leader_distance << '\n';
				targets.push_back(target(*t,value,target::VILLAGE));
			}
		}
	}

	std::vector<goal_ptr>& goals = get_goals();

	//find the enemy leaders and explicit targets
	unit_map::const_iterator u;
	if (get_leader_value()>0.0) {
		for(u = units_.begin(); u != units_.end(); ++u) {
			//is a visible enemy leader
			if (u->can_recruit() && current_team().is_enemy(u->side())
			    && !u->invisible(u->get_location())) {
				assert(map_.on_board(u->get_location()));
				LOG_AI << "found enemy leader (side: " << u->side() << ") target... " << u->get_location() << " with value: " << get_leader_value() << "\n";
				targets.push_back(target(u->get_location(), get_leader_value(), target::LEADER));
			}
		}

	}

	//explicit targets for this team
	for(std::vector<goal_ptr>::iterator j = goals.begin();
	    j != goals.end(); ++j) {

		if (!(*j)->active()) {
			continue;
		}
		(*j)->add_targets(std::back_inserter(targets));

	}

	//=== end getting targets

	std::vector<double> new_values;

	for(std::vector<target>::iterator i = targets.begin();
	    i != targets.end(); ++i) {

		new_values.push_back(i->value);

		for(std::vector<target>::const_iterator j = targets.begin(); j != targets.end(); ++j) {
			if(i->loc == j->loc) {
				continue;
			}

			const double distance = abs(j->loc.x - i->loc.x) +
						abs(j->loc.y - i->loc.y);
			new_values.back() += j->value/(distance*distance);
		}
	}

	assert(new_values.size() == targets.size());
	for(size_t n = 0; n != new_values.size(); ++n) {
		LOG_AI << "target value: " << targets[n].value << " -> " << new_values[n] << "\n";
		targets[n].value = new_values[n];
	}

	return targets;
}
Esempio n. 3
0
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_))
		gui_->enable_menu("endturn", 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_.empty())
					{
						const std::string msg =_("Undoing moves not yet transmitted to the server.");
						const int size = 20;
						const int lifetime = 150;
						SDL_Color colour = {255,255,255,255};

						SDL_Rect rect = gui_->map_area();
						font::add_floating_label(msg,size, colour,
								rect.w/2,rect.h/2,0.0,0.0,lifetime,rect,font::CENTER_ALIGN);
					}

					while(!undo_stack_.empty())
						menu_handler_.undo(gui_->get_playing_team() + 1);
					throw end_turn_exception(gui_->get_playing_team() + 1);
				}
			}

			play_slice();
			check_end_level();
		} catch(end_level_exception& e) {
			turn_data_->send_data();
			throw e;
		}

		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();

				throw end_turn_exception();
			}
		}

		gui_->draw();

		turn_data_->send_data();
	}
	menu_handler_.clear_undo_stack(player_number_);
}
Esempio n. 4
0
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;
}
Esempio n. 5
0
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;
}
Esempio n. 6
0
void playmp_controller::play_side()
{
	utils::string_map player;
	player["name"] = current_team().current_player();
	std::string turn_notification_msg = _("$name has taken control");
	turn_notification_msg = utils::interpolate_variables_into_string(turn_notification_msg, &player);
	gui_->send_notification(_("Turn changed"), turn_notification_msg);

	int end_ticks = calculate_end_ticks();

	player_type_changed_ = false;
	while (unit_map::main_ticks < end_ticks && !player_type_changed_) {
		VALIDATE(!unit::actor, "playmp_controller::play_side, unit::actor isn't NULL!");

		if (!skip_next_turn_) {
			end_turn_ = false;
		}

		unit* u = &units_.current_unit();
		if (!tent::turn_based) {
			int past_ticks = u->backward_ticks(u->ticks());
			if (unit_map::main_ticks + past_ticks >= end_ticks) {
				units_.do_escape_ticks_uh(teams_, *gui_, end_ticks - unit_map::main_ticks, false);
				autosave_ticks_ = -1;
				continue;
			}
			units_.do_escape_ticks_uh(teams_, *gui_, past_ticks, true);
		} else {
			if (u->side() < player_number_) {
				unit_map::main_ticks = end_ticks;
				player_number_ = 1;
				continue;
			}
		}
		// if exist spirit feature/technology, second maybe goto first. need reget
		u = &units_.current_unit();

		player_number_ = u->side(); // init_turn_data need player_number_ be valid.
		team& t = teams_[player_number_ - 1];

		bool local = t.is_local();
		bool new_side = false;
		if (local || loading_game_) {
			if (local) {
				init_turn_data();
			}
			new_side = do_prefix_unit(end_ticks, loading_game_, false);
		}

		if (t.is_network()) {
			play_network_turn();

		} else if (actor_can_continue_action(units_, player_number_)) {
			// we can't call playsingle_controller::play_side because
			// we need to catch exception here
			if (t.is_human()) {
				try {
					if (!loading_game_ && unit::actor->human_team_can_ai()) {
						play_ai_turn(turn_data_);
					}
					before_human_turn(new_side);
					play_human_turn();
					after_human_turn();
				} catch (end_turn_exception& end_turn) {
					if (end_turn.redo == player_number_ - 1) {
						player_type_changed_ = true;
						// if new controller is not human,
						// reset gui to prev human one
						if (!teams_[player_number_ - 2].is_human()) {
							browse_ = true;
							int t = find_human_team_before(player_number_ - 1);

							if (t <= 0)
								t = gui_->get_playing_team() + 1;

							gui_->set_team(t-1);
							gui_->recalculate_minimap();
							gui_->invalidate_all();
							gui_->draw(true,true);
						}
					} else {
						after_human_turn();
					}
				}

			} else if (t.is_ai()) {
				play_ai_turn(turn_data_);

			}
		}

		if (local) {
			do_post_unit(false);
			release_turn_data();
		}

		loading_game_ = false;
	}
	//keep looping if the type of a team (human/ai/networked) has changed mid-turn

	skip_next_turn_ = false;
}
Esempio n. 7
0
void play_controller::do_init_side()
{
    set_scontext_synced sync;
    log_scope("player turn");
    // In case we might end up calling sync:network during the side turn events,
    // and we don't want do_init_side to be called when a player drops.
    gamestate_->init_side_done() = true;
    init_side_done_now_ = true;

    const std::string turn_num = std::to_string(turn());
    const std::string side_num = std::to_string(current_side());

    gamestate().gamedata_.get_variable("side_number") = current_side();

    // We might have skipped some sides because they were empty so it is not enough to check for side_num==1
    if(!gamestate().tod_manager_.has_turn_event_fired())
    {
        pump().fire("turn_" + turn_num);
        pump().fire("new_turn");
        gamestate().tod_manager_.turn_event_fired();
    }

    pump().fire("side_turn");
    pump().fire("side_" + side_num + "_turn");
    pump().fire("side_turn_" + turn_num);
    pump().fire("side_" + side_num + "_turn_" + turn_num);

    // We want to work out if units for this player should get healed,
    // and the player should get income now.
    // Healing/income happen if it's not the first turn of processing,
    // or if we are loading a game.
    if (turn() > 1) {
        gamestate().board_.new_turn(current_side());
        current_team().new_turn();

        // If the expense is less than the number of villages owned
        // times the village support capacity,
        // then we don't have to pay anything at all
        int expense = gamestate().board_.side_upkeep(current_side()) -
                      current_team().support();
        if(expense > 0) {
            current_team().spend_gold(expense);
        }

        calculate_healing(current_side(), !is_skipping_replay());
    }

    // Prepare the undo stack.
    undo_stack().new_side_turn(current_side());

    pump().fire("turn_refresh");
    pump().fire("side_" + side_num + "_turn_refresh");
    pump().fire("turn_" + turn_num + "_refresh");
    pump().fire("side_" + side_num + "_turn_" + turn_num + "_refresh");

    // Make sure vision is accurate.
    actions::clear_shroud(current_side(), true);
    init_side_end();
    check_victory();
    sync.do_final_checkup();
}
Esempio n. 8
0
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 playsingle_controller::play_turn(bool save)
{
	resources::whiteboard->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(teams_.size()); ++player_number_)
	{
		// If a side is empty skip over it.
		if (current_team().is_empty()) continue;
		try {
			save_blocker blocker;
			init_side(player_number_ - 1);
		} catch (end_turn_exception) {
			if (current_team().is_network() == false) {
				turn_info turn_data(player_number_, replay_sender_,network_reader_);
				recorder.end_turn();
				turn_data.sync_network();
			}
			continue;
		}

		if (replaying_) {
			LOG_NG << "doing replay " << player_number_ << "\n";
			replaying_ = ::do_replay(player_number_) == REPLAY_FOUND_END_TURN;
			LOG_NG << "result of replay: " << (replaying_?"true":"false") << "\n";
		} else {
			// If a side is dead end the turn, but play at least side=1's
			// turn in case all sides are dead
			if (current_team().is_human() && side_units(player_number_) == 0
				&& (resources::units->size() != 0 || player_number_ != 1))
			{
				turn_info turn_data(player_number_, replay_sender_, network_reader_);
				recorder.end_turn();
				turn_data.sync_network();
				continue;
			}
			ai_testing::log_turn_start(player_number_);
			play_side(player_number_, save);
		}

		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_);
		}

		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>(teams_.size()))
		player_number_ = teams_.size();

	finish_turn();

	// Time has run out
	check_time_over();
}
Esempio n. 10
0
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();
	}
}
Esempio n. 11
0
possible_end_play_signal playmp_controller::play_side()
{
	mp_ui_alerts::turn_changed(current_team().current_player());
	// Proceed with the parent function.
	return playsingle_controller::play_side();
}
void playsingle_controller::before_human_turn(bool save)
{
	team& t = current_team();

	if (pause_when_human_) {
		browse_ = false;
	}
	linger_ = false;

	if (gui_->access_is_null(game_display::taccess_list::TROOP)) {
		// change resolution will result to enter it.
		gui_->refresh_access_troops(player_number_ - 1, game_display::REFRESH_RELOAD);
	}
	if (!tent::tower_mode() && !preferences::developer()) {
		gui_->refresh_access_troops(player_number_ - 1, game_display::REFRESH_HIDE, NULL);
	}
	gui_->refresh_access_heros(player_number_ - 1, game_display::REFRESH_RELOAD);
	ai::manager::raise_turn_started();

	if (tent::mode != mode_tag::LAYOUT && save) {
		autosave_ticks_ = unit_map::main_ticks;

		config snapshot;
		to_config(snapshot);
		savegame::autosave_savegame save(heros_, heros_start_, gamestate_, *gui_, snapshot);
		save.autosave(game_config::disable_autosave, preferences::autosavemax(), preferences::INFINITE_AUTO_SAVES);

		if (tent::turn_based) {
			const std::vector<hero*>& last_active_tactic = t.last_active_tactic();
			for (std::vector<hero*>::const_iterator it = last_active_tactic.begin(); it != last_active_tactic.end(); ++ it) {
				hero& selected_hero = **it;
				unit* tactician = units_.find_unit(selected_hero);
				if (tactician && !tactician->is_resided()) {
					do_add_active_tactic(*tactician, selected_hero, true);
				}
			}
		}

		// autosave ticks changed, refresh top panel.
		gui_->invalidate_game_status();

	} else {
		teams_[player_number_ - 1].refresh_tactic_slots(*gui_);
	}

	if (unit::actor && unit::actor->is_city()) {
		if (preferences::turn_bell()) {
			sound::play_bell(game_config::sounds::turn_bell);
		}
		if (game_config::show_side_report) {
			gui2::tside_report dlg(*gui_, teams_, units_, heros_, unit::actor->side());
			dlg.show(gui_->video());
		}
	}

	// button: undo
	undo_stack_.clear();
	if (teams_[player_number_-1].uses_shroud() || teams_[player_number_-1].uses_fog()) {
		gui_->set_theme_object_visible("undo", gui2::twidget::INVISIBLE);
	} else {
		gui_->set_theme_object_visible("undo", gui2::twidget::VISIBLE);
		gui_->set_theme_object_active("undo", false);
	}
	// button: endturn
	if (tent::mode == mode_tag::SIEGE) {
		if (pause_when_human_) {
			refresh_endturn_button(*gui_, "buttons/ctrl-play.png");
			gui_->set_theme_object_active("endturn", true);
		}
		
	} else if (tent::mode != mode_tag::LAYOUT) {
		gui_->set_theme_object_active("card", true);
		gui_->set_theme_object_active("endturn", true);
	} else {
		gui_->set_theme_object_visible("card", gui2::twidget::INVISIBLE);
		gui_->set_theme_object_visible("endturn", gui2::twidget::INVISIBLE);
	}
	
	// card
	refresh_card_button(t, *gui_);
	// if (!network::nconnections()) {
	//	gui_->show_context_menu();
	// } else {
		// Multiplayer mod, there is context-button when network player playing.
		// In order to display correct, need hide "before" button.
		gui_->goto_main_context_menu();
	// }
}
Esempio n. 13
0
bool ai_default_recruitment_stage::recruit_usage(const std::string& usage)
{
	raise_user_interact();
	analyze_all();

	const int min_gold = 0;

	log_scope2(log_ai, "recruiting troops");
	LOG_AI << "recruiting '" << usage << "'\n";

	//make sure id, usage and cost are known for the coming evaluation of unit types
	unit_types.build_all(unit_type::HELP_INDEX);

	std::vector<std::string> options;
	bool found = false;
	// Find an available unit that can be recruited,
	// matches the desired usage type, and comes in under budget.
	BOOST_FOREACH(const std::string &name, current_team().recruits())
	{
		const unit_type *ut = unit_types.find(name);
		if (!ut) continue;
		// If usage is empty consider any unit.
		if (usage.empty() || ut->usage() == usage) {
			LOG_AI << name << " considered for " << usage << " recruitment\n";
			found = true;

			if (current_team().gold() - ut->cost() < min_gold) {
				LOG_AI << name << " rejected, cost too high (cost: " << ut->cost() << ", current gold: " << current_team().gold() <<", min_gold: " << min_gold << ")\n";
				continue;
			}

			if (not_recommended_units_.count(name))
			{
				LOG_AI << name << " rejected, bad terrain or combat\n";
				continue;
			}


			std::map<std::string,int>::iterator imc = maximum_counts_.find(name);

			if (imc != maximum_counts_.end()) {
				int count_active = 0;
				for (unit_map::const_iterator u = resources::units->begin(); u != resources::units->end(); ++u) {
					if (u->side() == get_side() && !u->incapacitated() && u->type().base_id() == name) {
						++count_active;
					}
				}

				if (count_active >= imc->second) {
					LOG_AI << name << " rejected, too many in the field\n";
					continue;
				}
			}

			LOG_AI << "recommending '" << name << "'\n";
			options.push_back(name);
		}
	}

	// From the available options, choose one at random
	if(options.empty() == false) {
		const int option = rand()%options.size();
		recruit_result_ptr recruit_res = check_recruit_action(options[option]);
		if (recruit_res->is_ok()) {
			recruit_res->execute();
			if (!recruit_res->is_ok()) {
				ERR_AI << "recruitment failed "<< std::endl;
			}
		}
		return recruit_res->is_gamestate_changed();
	}
	if (found) {
		LOG_AI << "No available units to recruit that come under the price.\n";
	} else if (usage != "")	{
		//FIXME: This message should be suppressed when WML author
		//chooses the default recruitment pattern.
		const std::string warning = "At difficulty level " +
			resources::gamedata->difficulty() + ", trying to recruit a:" +
			usage + " but no unit of that type (usage=) is"
			" available. Check the recruit and [ai]"
			" recruitment_pattern keys for team '" +
			current_team().name() + "' (" +
			lexical_cast<std::string>(get_side()) + ")"
			" against the usage key of the"
			" units in question! Removing invalid"
			" recruitment_pattern entry and continuing...\n";
		WRN_AI << warning;
		// Uncommented until the recruitment limiting macro can be fixed to not trigger this warning.
		//lg::wml_error << warning;
		//@fixme
		//return current_team_w().remove_recruitment_pattern_entry(usage);
		return false;
	}
	return false;
}
/**
 * There is no real hope for us: we should try to do some damage to the enemy.
 * We can spend some cycles here, since it's rare.
 */
bool ai_default::desperate_attack(const map_location &loc)
{
	const unit &u = units_.find(loc)->second;
	LOG_AI << "desperate attack by '" << u.type_id() << "' " << loc << "\n";

	map_location adj[6];
	get_adjacent_tiles(loc, adj);

	double best_kill_prob = 0.0;
	unsigned int best_weapon = 0;
	unsigned best_dir = 0;

	for (unsigned n = 0; n != 6; ++n)
	{
		const unit *enemy = get_visible_unit(units_, adj[n], current_team());
		if (!enemy || !current_team().is_enemy(enemy->side()) || enemy->incapacitated())
			continue;
		const std::vector<attack_type> &attacks = u.attacks();
		for (unsigned i = 0; i != attacks.size(); ++i)
		{
			// Skip weapons with attack_weight=0
			if (attacks[i].attack_weight() == 0)
				continue;
			battle_context bc(units_, loc, adj[n], i);
			combatant att(bc.get_attacker_stats());
			combatant def(bc.get_defender_stats());
			att.fight(def);
			if (def.hp_dist[0] <= best_kill_prob)
				continue;
			best_kill_prob = def.hp_dist[0];
			best_weapon = i;
			best_dir = n;
		}
	}

	if (best_kill_prob > 0.0) {
		attack_result_ptr attack_res = execute_attack_action(loc, adj[best_dir], best_weapon);
		if (!attack_res->is_ok()) {
			ERR_AI << "desperate attack failed"<<std::endl;
		}
		return attack_res->is_gamestate_changed();
	}

	double least_hp = u.hitpoints() + 1;

	// Who would do most damage to us when they attack?  (approximate: may be different ToD)
	for (unsigned n = 0; n != 6; ++n)
	{
		const unit *enemy = get_visible_unit(units_, adj[n], current_team());
		if (!enemy || !current_team().is_enemy(enemy->side()) || enemy->incapacitated())
			continue;
		const std::vector<attack_type> &attacks = enemy->attacks();
		for (unsigned i = 0; i != attacks.size(); ++i)
		{
			// SKip weapons with attack_weight=0
			if (attacks[i].attack_weight() == 0)
				continue;
			battle_context bc(units_, adj[n], loc, i);
			combatant att(bc.get_attacker_stats());
			combatant def(bc.get_defender_stats());
			att.fight(def);
			if (def.average_hp() < least_hp) {
				least_hp = def.average_hp();
				best_dir = n;
			}
		}
	}

	// It is possible that there were no adjacent units to attack...
	if (least_hp != u.hitpoints() + 1) {
		battle_context bc(units_, loc, adj[best_dir], -1, -1, 0.5);
		attack_result_ptr attack_res = execute_attack_action(loc, adj[best_dir], bc.get_attacker_stats().attack_num);
		if (!attack_res->is_ok()) {
			ERR_AI << "desperate attack failed" << std::endl;
		}
		return attack_res->is_gamestate_changed();
	}
	return false;
}
Esempio n. 15
0
void protect_goal::add_targets(std::back_insert_iterator< std::vector< target >> target_list)
{
	std::string goal_type;
	if (protect_unit_) {
		goal_type = "protect_unit";
	} else {
		goal_type ="protect_location";
	}

	if (!(this)->active()) {
		LOG_AI_GOAL << "skipping " << goal_type << " goal - not active" << std::endl;
		return;
	}

	const config &criteria = cfg_.child("criteria");
	if (!criteria) {
		LOG_AI_GOAL << "skipping " << goal_type << " goal - no criteria given" << std::endl;
		return;
	} else {
		DBG_AI_GOAL << "side " << get_side() << ": "<< goal_type << " goal with criteria" << std::endl << cfg_.child("criteria") << std::endl;
	}

	unit_map &units = resources::gameboard->units();

	std::set<map_location> items;
	if (protect_unit_) {
		const unit_filter ufilt{ vconfig(criteria) };
		for (const unit &u : units)
		{
			// 'protect_unit' can be set to any unit of any side -> exclude hidden units
			// unless they are visible to the AI side (e.g. allies with shared vision).
			// As is done in other parts of the AI, units under fog/shroud count as visible to the AI.
			if (ufilt(u)
				&& (!u.invisible(u.get_location()) || u.is_visible_to_team(current_team(), false)))
			{
				DBG_AI_GOAL << "side " << get_side() << ": in " << goal_type << ": " << u.get_location() << " should be protected\n";
				items.insert(u.get_location());
			}
		}
	} else {
		filter_ptr_->get_locations(items);
	}
	DBG_AI_GOAL << "side " << get_side() << ": searching for threats in "+goal_type+" goal" << std::endl;
	// Look for directions to protect a specific location or specific unit.
	for (const map_location &loc : items)
	{
		for (const unit &u : units)
		{
			int distance = distance_between(u.get_location(), loc);
			if (current_team().is_enemy(u.side()) && distance < radius_ &&
			    !u.invisible(u.get_location()))
			{
				DBG_AI_GOAL << "side " << get_side() << ": in " << goal_type << ": found threat target. " << u.get_location() << " is a threat to "<< loc << '\n';
				*target_list = target(u.get_location(),
					value_ * static_cast<double>(radius_ - distance) /
					radius_, target::TYPE::THREAT);
			}
		}
	}


}
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;
}
Esempio n. 17
0
void protect_goal::add_targets(std::back_insert_iterator< std::vector< target > > target_list)
{
	std::string goal_type;
	if (protect_unit_) {
		if (protect_only_own_unit_) {
			goal_type = "protect_my_unit";
		} else {
			goal_type = "protect_unit";
		}
	} else {
		goal_type ="protect_location";
	}

	if (!(this)->active()) {
		LOG_AI_GOAL << "skipping " << goal_type << " goal - not active" << std::endl;
		return;
	}

	const config &criteria = cfg_.child("criteria");
	if (!criteria) {
		LOG_AI_GOAL << "skipping " << goal_type << " goal - no criteria given" << std::endl;
		return;
	} else {
		DBG_AI_GOAL << goal_type << " goal with criteria" << std::endl << cfg_.child("criteria") << std::endl;
	}


	unit_map &units = get_info().units;
	std::vector<team> &teams = get_info().teams;


	std::set<map_location> items;
	if (protect_unit_) {
		for(unit_map::const_iterator u = units.begin(); u != units.end(); ++u) {

			if (protect_only_own_unit_ && u->second.side()!=get_side()) {
				continue;
			}
			//TODO: we will protect hidden units, by not testing for invisibility to current side
			if (u->second.matches_filter(vconfig(criteria),u->first)) {
				DBG_AI_GOAL << "in "<<goal_type<< ": " << u->first << " should be protected " << std::endl;
				items.insert(u->first);
			}
		}
	} else {
		filter_ptr_->get_locations(items);
	}
	DBG_AI_GOAL << "seaching for threats in "+goal_type+" goal" << std::endl;
	// Look for directions to protect a specific location or specific unit.
	foreach (const map_location &loc, items)
	{
		for(unit_map::const_iterator u = units.begin(); u != units.end(); ++u) {
			const int distance = distance_between(u->first,loc);
			if(current_team().is_enemy(u->second.side()) && distance < radius_
			&& !u->second.invisible(u->first, units, teams)) {
				DBG_AI_GOAL << "in "<<goal_type<<": found threat target. " << u->first << " is a threat to "<< loc << std::endl;
				*target_list = target(u->first, value_ * double(radius_-distance) /
							double(radius_),target::THREAT);
			}
		}
	}


}
Esempio n. 18
0
void playmp_controller::play_human_turn()
{
	command_disabled_resetter reset_commands;
	int cur_ticks = SDL_GetTicks();
	show_turn_dialog();
	execute_gotos();

	bool auto_end_turn = can_auto_end_turn(true);
	if (!auto_end_turn) {
		if (!unit::actor->is_city() && unit::actor->task() == unit::TASK_NONE) {
			gui_->scroll_to_tile(unit::actor->get_location(), game_display::ONSCREEN, true, true);
		}
	} else {
		return;
	}

	if ((!linger_) || (is_host_)) {
		gui_->set_theme_object_active("endturn", true);
	}

	while (!end_turn_ && !auto_end_turn) {
		try {
			play_slice();
			check_end_level();

			std::deque<config> backlog;
			std::vector<config> cfgs = received_data_cfg_;
			received_data_cfg_.clear();
			for (std::vector<config>::const_iterator it = cfgs.begin(); it != cfgs.end(); ++ it) {
				const config& cfg = *it;
				if (turn_data_->process_network_data(cfg, lobby->transit.conn(), backlog, skip_replay_) == turn_info::PROCESS_RESTART_TURN) {
					// Clean undo stack if turn has to be restarted (losing control)
					if (!undo_stack_.empty()) {
						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_.empty()) {
						menu_handler_.undo(gui_->get_playing_team() + 1);
					}
					throw end_turn_exception(gui_->get_playing_team() + 1);
				}
			}

		} catch (end_level_exception& e) {
			turn_data_->send_data();
			throw e;
		}

		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();

				throw end_turn_exception();
			}
		}

		gui_->draw();

		turn_data_->send_data();

		auto_end_turn = can_auto_end_turn(false);
	}
	menu_handler_.clear_undo_stack(player_number_);
}