예제 #1
0
bool can_generate(const gamemap& map, const std::vector<team>& teams, const unit_map& units, const unit& u, const map_location& loc)
{
	if (!map.on_board(loc)) {
		return false;
	}
	if (u.movement_cost(map[loc]) == unit_movement_type::UNREACHABLE) {
		return false;
	}
	unit_map::const_iterator it = units.find(loc, false);
	if (it.valid() && !it->can_stand(u)) {
		return false;
	}

	map_location locs[6];
	get_adjacent_tiles(loc, locs);
	for (int i = 0; i != 6; ++i) {
		if (!map.on_board(locs[i])) {
			continue;
		}
		if (u.movement_cost(map[locs[i]]) != unit_movement_type::UNREACHABLE) {
			return true;
		}
	}
	return false;
}
예제 #2
0
// Complexity: O(nr lg(nr) + nr^2), where n = locs.size(), r = radius.
// The nr^2 term is bounded by the size of the board.
void get_tiles_radius(const gamemap& map, const std::vector<map_location>& locs,
                      size_t radius, std::set<map_location>& result,
                      bool with_border)
{
	// Make sure the provided locations are included.
	// This would be needed in case some of the provided locations are off-map.
	// It also allows simpler processing if the radius is zero.
	// For efficiency, do this first since locs is potentially unsorted.
	result.insert(locs.begin(), locs.end());

	if ( radius != 0  &&  !locs.empty() )
	{
		const int border = with_border ? map.border_size() : 0;
		column_ranges collected_tiles;

		// Collect the hexes within the desired disks into collected_tiles.
		// This maps each x-value to a set of ranges of y-values that
		// are covered by the disks around each element of locs.
		// (So the data size at this point is proportional to the number
		// of x-values involved, which is O(nr). The lg(nr) factor comes
		// from the data being sorted.)
		get_column_ranges(collected_tiles, locs, radius, -border, map.w() + border);

		// Now that all the tiles have been collected, add them to result.
		// (There are O(nr^2) hexes to add.) By collecting before adding, each
		// hex will be processed only once, even when disks overlap. This is
		// how we can get good performance if there is significant overlap, and
		// how the work required can be bound by the size of the board.
		ranges_to_tiles(result, collected_tiles, -border, map.h() + border);
	}
}
예제 #3
0
boost::optional<std::string> game_board::replace_map(const gamemap & newmap) {
	boost::optional<std::string> ret = boost::optional<std::string> ();

	/* Remember the locations where a village is owned by a side. */
	std::map<map_location, int> villages;
	for(const auto& village : map_->villages()) {
		const int owner = village_owner(village);
		if(owner != -1) {
			villages[village] = owner;
		}
	}

	for (unit_map::iterator itor = units_.begin(); itor != units_.end(); ) {
		if (!newmap.on_board(itor->get_location())) {
			if (!try_add_unit_to_recall_list(itor->get_location(), itor.get_shared_ptr())) {
				*ret = std::string("replace_map: Cannot add a unit that would become off-map to the recall list\n");
			}
			units_.erase(itor++);
		} else {
			++itor;
		}
	}

	/* Disown villages that are no longer villages. */
	for(const auto& village : villages) {
		if(!newmap.is_village(village.first)) {
			teams_[village.second].lose_village(village.first);
		}
	}

	*map_ = newmap;
	return ret;
}
예제 #4
0
bool move_result::test_route(const unit &un, const team &my_team, const unit_map &units, const std::vector<team> &teams, const gamemap &map, bool)
{
	if (from_== to_) {
		if (!remove_movement_ || (un.movement_left() == 0) ) {
			set_error(E_EMPTY_MOVE);
			return false;
		}
		return true;
	}

	if (un.movement_left() == 0 ) {
		set_error(E_EMPTY_MOVE);
		return false;
	}

	if (!to_.valid()) {
		set_error(E_NO_ROUTE);
		return false;
	}
	const pathfind::shortest_path_calculator calc(un, my_team, units, teams,map);

	//allowed teleports
	std::set<map_location> allowed_teleports = pathfind::get_teleport_locations(un, units, my_team, true);//@todo 1.9: see_all -> false

	//do an A*-search
	route_ = pathfind::a_star_search(un.get_location(), to_, 10000.0, &calc, map.w(), map.h(), &allowed_teleports);
	if (route_.steps.empty()) {
		set_error(E_NO_ROUTE);
		return false;
	}
	return true;
}
예제 #5
0
time_of_day tod_manager::time_of_day_at(const unit_map& units,const map_location& loc, const gamemap& map) const
{
	int lighten = std::max<int>(map.get_terrain_info(map.get_terrain(loc)).light_modification() , 0);
	int darken = std::min<int>(map.get_terrain_info(map.get_terrain(loc)).light_modification() , 0);

	time_of_day tod = get_time_of_day(lighten + darken,loc);

	if(loc.valid()) {
		map_location locs[7];
		locs[0] = loc;
		get_adjacent_tiles(loc,locs+1);

		for(int i = 0; i != 7; ++i) {
			const unit_map::const_iterator itor = units.find(locs[i]);
			if(itor != units.end() &&
			    itor->second.get_ability_bool("illuminates") &&
			    !itor->second.incapacitated())
			{
				unit_ability_list illum = itor->second.get_abilities("illuminates");
				unit_abilities::effect illum_effect(illum,lighten,false);
				int mod = illum_effect.get_composite_value();
				if(mod + tod.lawful_bonus > illum.highest("max_value").first) {
					mod = illum.highest("max_value").first - tod.lawful_bonus;
				}
				lighten = std::max<int>(mod, lighten);
				darken = std::min<int>(mod, darken);
			}
		}
	}
	tod = get_time_of_day(lighten + darken,loc);

	return tod;
}
예제 #6
0
void team::build(const config& cfg, const gamemap& map, int gold)
{
	gold_ = gold;
	info_.read(cfg);

	fog_.set_enabled(cfg["fog"].to_bool());
	fog_.read(cfg["fog_data"]);
	shroud_.set_enabled(cfg["shroud"].to_bool());
	shroud_.read(cfg["shroud_data"]);
	auto_shroud_updates_ = cfg["auto_shroud"].to_bool(auto_shroud_updates_);

	LOG_NG << "team::team(...): team_name: " << info_.team_name << ", shroud: " << uses_shroud()
		   << ", fog: " << uses_fog() << ".\n";

	// Load the WML-cleared fog.
	const config& fog_override = cfg.child("fog_override");
	if(fog_override) {
		const std::vector<map_location> fog_vector
				= map.parse_location_range(fog_override["x"], fog_override["y"], true);
		fog_clearer_.insert(fog_vector.begin(), fog_vector.end());
	}

	// To ensure some minimum starting gold,
	// gold is the maximum of 'gold' and what is given in the config file
	gold_ = std::max(gold, info_.gold);
	if(gold_ != info_.gold) {
		info_.start_gold = gold;
	}

	// Old code was doing:
	// info_.start_gold = std::to_string(gold) + " (" + info_.start_gold + ")";
	// Was it correct?

	// Load in the villages the side controls at the start
	for(const config& v : cfg.child_range("village")) {
		map_location loc(v);
		if(map.is_village(loc)) {
			villages_.insert(loc);
		} else {
			WRN_NG << "[side] " << current_player() << " [village] points to a non-village location " << loc
				   << std::endl;
		}
	}

	countdown_time_ = cfg["countdown_time"];
	action_bonus_count_ = cfg["action_bonus_count"];

	planned_actions_.reset(new wb::side_actions());
	planned_actions_->set_team_index(info_.side - 1);
}
예제 #7
0
파일: pathfind.cpp 프로젝트: ehsan/wesnoth
map_location pathfind::find_vacant_tile(const gamemap& map,
				const unit_map& units,
				const map_location& loc,
				pathfind::VACANT_TILE_TYPE vacancy,
				const unit* pass_check)
{
	if (!map.on_board(loc)) return map_location();
	std::set<map_location> pending_tiles_to_check, tiles_checked;
	pending_tiles_to_check.insert(loc);
	// Iterate out 50 hexes from loc
	for (int distance = 0; distance < 50; ++distance) {
		if (pending_tiles_to_check.empty())
			return map_location();
		//Copy over the hexes to check and clear the old set
		std::set<map_location> tiles_checking;
		tiles_checking.swap(pending_tiles_to_check);
		//Iterate over all the hexes we need to check
		foreach (const map_location &loc, tiles_checking)
		{
			//If this area is not a castle but should, skip it.
			if (vacancy == pathfind::VACANT_CASTLE && !map.is_castle(loc)) continue;
			const bool pass_check_and_unreachable = pass_check
				&& pass_check->movement_cost(map[loc]) == unit_movement_type::UNREACHABLE;
			//If the unit can't reach the tile and we have searched
			//an area of at least radius 10 (arbitrary), skip the tile.
			//Neccessary for cases such as an unreachable
			//starting hex surrounded by 6 other unreachable hexes, in which case
			//the algorithm would not even search distance==1
			//even if there's a reachable hex for distance==2.
			if (pass_check_and_unreachable && distance > 10) continue;
			//If the hex is empty and we do either no pass check or the hex is reachable, return it.
			if (units.find(loc) == units.end() && !pass_check_and_unreachable) return loc;
			map_location adjs[6];
			get_adjacent_tiles(loc,adjs);
			foreach (const map_location &loc, adjs)
			{
				if (!map.on_board(loc)) continue;
				// Add the tile to be checked if it hasn't already been and
				// isn't being checked.
				if (tiles_checked.find(loc) == tiles_checked.end() &&
				    tiles_checking.find(loc) == tiles_checking.end())
				{
					pending_tiles_to_check.insert(loc);
				}
			}
		}
		tiles_checked.swap(tiles_checking);
	}
	return map_location();
}
예제 #8
0
void get_tiles_radius(gamemap const &map, std::vector<map_location> const &locs,
                      size_t radius, std::set<map_location> &res, xy_pred *pred)
{
	typedef std::set<map_location> location_set;
	location_set not_visited(locs.begin(), locs.end()), must_visit, filtered_out;
	++radius;

	for(;;) {
		location_set::const_iterator it = not_visited.begin(), it_end = not_visited.end();
		std::copy(it,it_end,std::inserter(res,res.end()));
		for(; it != it_end; ++it) {
			map_location adj[6];
			get_adjacent_tiles(*it, adj);
			for(size_t i = 0; i != 6; ++i) {
				map_location const &loc = adj[i];
				if(map.on_board(loc) && !res.count(loc) && !filtered_out.count(loc)) {
					if(!pred || (*pred)(loc)) {
						must_visit.insert(loc);
					} else {
						filtered_out.insert(loc);
					}
				}
			}
		}

		if(--radius == 0 || must_visit.empty()) {
			break;
		}

		not_visited.swap(must_visit);
		must_visit.clear();
	}
}
예제 #9
0
gamemap editor_map::mask_to(const gamemap& target) const
{
	if (target.w() != w() || target.h() != h()) {
		throw editor_action_exception(_("The size of the target map is different from the current map"));
	}
	gamemap mask(target);
	map_location iter;
	for (iter.x = -border_size(); iter.x < w() + border_size(); ++iter.x) {
		for (iter.y = -border_size(); iter.y < h() + border_size(); ++iter.y) {
			if (target.get_terrain(iter) == get_terrain(iter)) {
				mask.set_terrain(iter, t_translation::FOGGED);
			}
		}
	}
	return mask;
}
예제 #10
0
파일: mapgen.cpp 프로젝트: x2f/wesciv
void add_river(river_ptr river, const height_map& heights, gamemap& m, const corner_location& c1, const corner_location& c2)
{
	location adj1[3];
	location adj2[3];
	
	std::vector<location> locs;
	get_adjacent_hexes_to_corner(c1, adj1);
	get_adjacent_hexes_to_corner(c2, adj2);
	for(int i = 0; i != 3; ++i) {
		for(int j = 0; j != 3; ++j) {
			if(adj1[i] == adj2[j]) {
				locs.push_back(adj1[i]);
			}
		}
	}

	assert(locs.size() == 2);

	DIRECTION dir1 = get_adjacent_direction(locs[0], locs[1]);
	DIRECTION dir2 = get_adjacent_direction(locs[1], locs[0]);
	assert(dir1 != NULL_DIRECTION);
	assert(dir2 != NULL_DIRECTION);

	tile_ptr t1 = m.get_tile(locs[0]);
	tile_ptr t2 = m.get_tile(locs[1]);

	t1->add_river(dir1, river);
	t2->add_river(dir2, river);
}
예제 #11
0
map_location find_vacant_tile(const gamemap& map,
				const unit_map& units,
				const map_location& loc,
				VACANT_TILE_TYPE vacancy,
				const unit* pass_check)
{
	std::set<map_location> pending_tiles_to_check;
	std::set<map_location> tiles_checked;
	pending_tiles_to_check.insert( loc );
	// Iterate out 50 hexes from loc
	for (int distance = 0; distance < 50; ++distance) {
		if (pending_tiles_to_check.empty())
			return map_location();
		//Copy over the hexes to check and clear the old set
		std::set<map_location> tiles_checking = pending_tiles_to_check;
		std::set<map_location>::const_iterator tc_itor = tiles_checking.begin();
		pending_tiles_to_check.clear();
		//Iterate over all the hexes we need to check
		for ( ; tc_itor != tiles_checking.end(); ++tc_itor )
		{
			//If the unit cannot reach this area or it's not a castle but should, skip it.
			if ((vacancy == VACANT_CASTLE && !map.is_castle(*tc_itor))
			|| (pass_check && pass_check->movement_cost(map[*tc_itor])
					== unit_movement_type::UNREACHABLE))
				continue;
			//If the hex is empty, return it.
			if (map.on_board(*tc_itor) && units.find(*tc_itor) == units.end())
				return (*tc_itor);
			map_location adjs[6];
			get_adjacent_tiles(*tc_itor,adjs);
			for (int i = 0; i != 6; ++i)
			{
				//Add the tile to be checked if it hasn't already been and isn't already
				//pending to be checked
				if (pending_tiles_to_check.find(adjs[i]) == pending_tiles_to_check.end() &&
					tiles_checked.find(adjs[i]) == tiles_checked.end() &&
					tiles_checking.find(adjs[i]) == tiles_checking.end())
				{
					pending_tiles_to_check.insert(adjs[i]);
				}
			}
		}
		tiles_checked = tiles_checking;
	}
	return map_location();
}
예제 #12
0
bool recruit_result::test_leader_on_keep(const gamemap &map, const unit &my_leader, bool)
{
	if (!map.is_keep(my_leader.get_location())) {
		set_error(E_LEADER_NOT_ON_KEEP);
		return false;
	}
	return true;
}
map_location pathfind::find_vacant_tile(const gamemap& map,
				const unit_map& units,
				const map_location& loc,
				pathfind::VACANT_TILE_TYPE vacancy,
				const unit* pass_check)
{
	if (!map.on_board(loc)) return map_location();
	std::set<map_location> pending_tiles_to_check, tiles_checked;
	pending_tiles_to_check.insert(loc);
	// Iterate out 50 hexes from loc
	for (int distance = 0; distance < 50; ++distance) {
		if (pending_tiles_to_check.empty())
			return map_location();
		//Copy over the hexes to check and clear the old set
		std::set<map_location> tiles_checking;
		tiles_checking.swap(pending_tiles_to_check);
		//Iterate over all the hexes we need to check
		foreach (const map_location &loc, tiles_checking)
		{
			//If the unit cannot reach this area or it's not a castle but should, skip it.
			if ((vacancy == pathfind::VACANT_CASTLE && !map.is_castle(loc))
			|| (pass_check && pass_check->movement_cost(map[loc])
					== unit_movement_type::UNREACHABLE))
				continue;
			//If the hex is empty, return it.
			if (units.find(loc) == units.end())
				return loc;
			map_location adjs[6];
			get_adjacent_tiles(loc,adjs);
			foreach (const map_location &loc, adjs)
			{
				if (!map.on_board(loc)) continue;
				// Add the tile to be checked if it hasn't already been and
				// isn't being checked.
				if (tiles_checked.find(loc) == tiles_checked.end() &&
				    tiles_checking.find(loc) == tiles_checking.end())
				{
					pending_tiles_to_check.insert(loc);
				}
			}
		}
		tiles_checked.swap(tiles_checking);
	}
	return map_location();
}
예제 #14
0
map_location pathfind::find_vacant_tile(const gamemap& map,
				const std::vector<team>& teams,
				const unit_map& units,
				const map_location& loc,
				const unit* pass_check)
{
	if (!map.on_board(loc)) return map_location();
	std::set<map_location> pending_tiles_to_check, tiles_checked;
	pending_tiles_to_check.insert(loc);
	// Iterate out 50 hexes from loc
	for (int distance = 0; distance < 50; ++distance) {
		//Copy over the hexes to check and clear the old set
		std::set<map_location> tiles_checking;
		tiles_checking.swap(pending_tiles_to_check);
		//Iterate over all the hexes we need to check
		BOOST_FOREACH (const map_location &loc, tiles_checking)
		{
			tiles_checked.insert(loc);

			// If the unit cannot reach this area or it's not a castle but should, skip it.
			if (!pass_check || can_generate(map, teams, units, *pass_check, loc)) {
				// If the hex is empty, return it.
				if (units.find(loc) == units.end()) {
					return loc;
				}
			}
			map_location adjs[6];
			get_adjacent_tiles(loc, adjs);
			BOOST_FOREACH (const map_location &loc, adjs)
			{
				if (!map.on_board(loc)) continue;
				// Add the tile to be checked if it hasn't already been and
				// isn't being checked.
				if (tiles_checked.find(loc) == tiles_checked.end()) {
					pending_tiles_to_check.insert(loc);
				}
			}			
		}
	}
예제 #15
0
void tod_manager::add_time_area(const gamemap & map, const config& cfg)
{
	areas_.push_back(area_time_of_day());
	area_time_of_day &area = areas_.back();
	area.id = cfg["id"].str();
	area.xsrc = cfg["x"].str();
	area.ysrc = cfg["y"].str();
	area.currentTime = cfg["current_time"].to_int(0);
	std::vector<map_location> const& locs (map.parse_location_range(area.xsrc, area.ysrc, true));
	area.hexes.insert(locs.begin(), locs.end());
	time_of_day::parse_times(cfg, area.times);
	has_tod_bonus_changed_ = true;
}
예제 #16
0
/**
 * Function that will add to @a result all elements of @a locs, plus all
 * on-board locations matching @a pred that are connected to elements of
 * locs by a chain of at most @a radius tiles, each of which matches @a pred.
 * @a result must be a std::set of locations.
 */
void get_tiles_radius(gamemap const &map, std::vector<map_location> const &locs,
                      size_t radius, std::set<map_location> &result,
                      bool with_border, xy_pred const &pred)
{
	typedef std::set<map_location> location_set;

	location_set must_visit, filtered_out;
	location_set not_visited(locs.begin(), locs.end());

	for ( ; radius != 0  &&  !not_visited.empty(); --radius )
	{
		location_set::const_iterator it = not_visited.begin();
		location_set::const_iterator it_end = not_visited.end();

		result.insert(it, it_end);
		for(; it != it_end; ++it) {
			map_location adj[6];
			get_adjacent_tiles(*it, adj);
			for(size_t i = 0; i != 6; ++i) {
				map_location const &loc = adj[i];
				if ( with_border ? map.on_board_with_border(loc) :
				                   map.on_board(loc) ) {
					if ( !result.count(loc) && !filtered_out.count(loc) ) {
						if ( pred(loc) )
							must_visit.insert(loc);
						else
							filtered_out.insert(loc);
					}
				}
			}
		}

		not_visited.swap(must_visit);
		must_visit.clear();
	}

	result.insert(not_visited.begin(), not_visited.end());
}
예제 #17
0
bool recruit_result::test_suitable_recruit_location(const gamemap &map, const unit_map &units, const unit &my_leader, bool)
{
	recruit_location_ = where_;

	//if we have not-on-board location, such as null_location, then the caller wants us to recruit on 'any' possible tile.
	if (!map.on_board(recruit_location_)) {
		recruit_location_ = pathfind::find_vacant_tile(map, units, my_leader.get_location(), pathfind::VACANT_CASTLE);
	}

	if (!can_recruit_on(map, my_leader.get_location(), recruit_location_)) {
		set_error(E_BAD_RECRUIT_LOCATION);
		return false;
	}
	return true;
}
예제 #18
0
static int placing_score(const config& side, const gamemap& map, const map_location& pos)
{
	int positions = 0, liked = 0;
	const t_translation::t_list terrain = t_translation::read_list(side["terrain_liked"]);

	for(int i = pos.x-8; i != pos.x+8; ++i) {
		for(int j = pos.y-8; j != pos.y+8; ++j) {
			const map_location pos(i,j);
			if(map.on_board(pos)) {
				++positions;
				if(std::count(terrain.begin(),terrain.end(),map[pos])) {
					++liked;
				}
			}
		}
	}

	return (100*liked)/positions;
}
예제 #19
0
marked_route mark_route(const plain_route &rt,
	const std::vector<map_location>& waypoints, const unit &u,
	const team &viewing_team, const unit_map &units,
	const std::vector<team> &teams, const gamemap &map)
{
	marked_route res;

	if (rt.steps.empty()) return res;
	res.steps = rt.steps;

	int turns = 0;
	int movement = u.movement_left();
	const team& unit_team = teams[u.side()-1];
	bool zoc = false;

	std::vector<map_location>::const_iterator i = rt.steps.begin(),
			w = waypoints.begin();

	// TODO fix the name confusion with waypoints and route.waypoints
	for (; i !=rt.steps.end(); i++) {
		bool last_step = (i+1 == rt.steps.end());

		// move_cost of the next step is irrelevant for the last step
		assert(last_step || map.on_board(*(i+1)));
		const int move_cost = last_step ? 0 : u.movement_cost(map[*(i+1)]);
		bool capture = false;
		bool pass_here = false;
		if (w != waypoints.end() && *i == *w) {
			w++;
			pass_here = true;
		}

		if (last_step || zoc || move_cost > movement) {
			// check if we stop an a village and so maybe capture it
			// if it's an enemy unit and a fogged village, we assume a capture
			// (if he already owns it, we can't know that)
			// if it's not an enemy, we can always know if he owns the village
			bool capture = map.is_village(*i) && ( !unit_team.owns_village(*i)
				 || (viewing_team.is_enemy(u.side()) && viewing_team.fogged(*i)) );

			++turns;

			bool invisible = u.invisible(*i,units,teams,false);

			res.waypoints[*i] = marked_route::waypoint(turns, pass_here, zoc, capture, invisible);

			if (last_step) break; // finished and we used dummy move_cost

			movement = u.total_movement();
			if(move_cost > movement) {
				return res; //we can't reach destination
			}
		} else if (pass_here) {
			bool invisible = u.invisible(*i,units,teams,false);
			res.waypoints[*i] = marked_route::waypoint(0, pass_here, zoc, false, invisible);
		}

		zoc = enemy_zoc(units, teams, *(i + 1), viewing_team,u.side())
					&& !u.get_ability_bool("skirmisher", *(i+1));

		if (zoc || capture) {
			movement = 0;
		} else {
			movement -= move_cost;
		}
	}

	return res;
}
예제 #20
0
void map_fragment::paste_into(gamemap& map, const map_location& loc) const
{
	for (const tile_info& i : items_) {
		map.set_terrain(i.offset.vector_sum(loc), i.terrain);
	}
}
예제 #21
0
const time_of_day tod_manager::get_illuminated_time_of_day(const unit_map & units, const gamemap & map, const map_location& loc, int for_turn) const
{
	// get ToD ignoring illumination
	time_of_day tod = get_time_of_day(loc, for_turn);

	if ( map.on_board_with_border(loc) )
	{
		// Now add terrain illumination.
		const int terrain_light = map.get_terrain_info(loc).light_bonus(tod.lawful_bonus);

		std::vector<int> mod_list;
		std::vector<int> max_list;
		std::vector<int> min_list;
		int most_add = 0;
		int most_sub = 0;

		// Find the "illuminates" effects from units that can affect loc.
		map_location locs[7];
		locs[0] = loc;
		get_adjacent_tiles(loc,locs+1);
		for ( size_t i = 0; i != 7; ++i ) {
			const unit_map::const_iterator itor = units.find(locs[i]);
			if (itor != units.end() &&
			    itor->get_ability_bool("illuminates") &&
			    !itor->incapacitated())
			{
				unit_ability_list illum = itor->get_abilities("illuminates");
				unit_abilities::effect illum_effect(illum, terrain_light, false);
				const int unit_mod = illum_effect.get_composite_value();

				// Record this value.
				mod_list.push_back(unit_mod);
				max_list.push_back(illum.highest("max_value").first);
				min_list.push_back(illum.lowest("min_value").first);
				if ( unit_mod > most_add )
					most_add = unit_mod;
				else if ( unit_mod < most_sub )
					most_sub = unit_mod;
			}
		}
		const bool net_darker = most_add < -most_sub;

		// Apply each unit's effect, tracking the best result.
		int best_result = terrain_light;
		const int base_light = terrain_light + (net_darker ? most_add : most_sub);
		for ( size_t i = 0; i != mod_list.size(); ++i ) {
			int result =
				bounded_add(base_light, mod_list[i], max_list[i], min_list[i]);

			if ( net_darker  &&  result < best_result )
				best_result = result;
			else if ( !net_darker  &&  result > best_result )
				best_result = result;
		}

		// Update the object we will return.
		tod.bonus_modified = best_result - tod.lawful_bonus;
		tod.lawful_bonus = best_result;
	}

	return tod;
}
예제 #22
0
static void find_routes(const gamemap& map, const unit_map& units,
		const unit& u, const map_location& loc,
		int move_left, paths::dest_vect &destinations,
		std::vector<team> const &teams,
		bool force_ignore_zocs, bool allow_teleport, int turns_left,
		const team &viewing_team,
		bool see_all, bool ignore_units)
{
	const team& current_team = teams[u.side() - 1];
	std::set<map_location> teleports;
	if (allow_teleport) {
		teleports = get_teleport_locations(u, units, viewing_team, see_all, ignore_units);
	}

	const int total_movement = u.total_movement();

	std::vector<map_location> locs(6 + teleports.size());
	std::copy(teleports.begin(), teleports.end(), locs.begin() + 6);

	search_counter += 2;
	if (search_counter == 0) search_counter = 2;

	static std::vector<node> nodes;
	nodes.resize(map.w() * map.h());

	indexer index(map.w(), map.h());
	comp node_comp(nodes);

	int xmin = loc.x, xmax = loc.x, ymin = loc.y, ymax = loc.y, nb_dest = 1;

	nodes[index(loc)] = node(move_left, turns_left, map_location::null_location, loc);
	std::vector<int> pq;
	pq.push_back(index(loc));

	while (!pq.empty()) {
		node& n = nodes[pq.front()];
		std::pop_heap(pq.begin(), pq.end(), node_comp);
		pq.pop_back();
		n.in = search_counter;

		get_adjacent_tiles(n.curr, &locs[0]);
		for (int i = teleports.count(n.curr) ? locs.size() : 6; i-- > 0; ) {
			if (!locs[i].valid(map.w(), map.h())) continue;

			node& next = nodes[index(locs[i])];

			bool next_visited = next.in - search_counter <= 1u;

			// Classic Dijkstra allow to skip chosen nodes (with next.in==search_counter)
			// But the cost function and hex grid allow to also skip visited nodes:
			// if next was visited, then we already have a path 'src-..-n2-next'
			// - n2 was chosen before n, meaning that it is nearer to src.
			// - the cost of 'n-next' can't be smaller than 'n2-next' because
			//   cost is independent of direction and we don't have more MP at n
			//   (important because more MP may allow to avoid waiting next turn)
			// Thus, 'src-..-n-next' can't be shorter.
			if (next_visited) continue;

			const int move_cost = u.movement_cost(map[locs[i]]);

			node t = node(n.movement_left, n.turns_left, n.curr, locs[i]);
			if (t.movement_left < move_cost) {
				t.movement_left = total_movement;
				t.turns_left--;
			}

			if (t.movement_left < move_cost || t.turns_left < 0) continue;

			t.movement_left -= move_cost;

			if (!ignore_units) {
				const unit *v =
					get_visible_unit(units, locs[i], viewing_team, see_all);
				if (v && current_team.is_enemy(v->side()))
					continue;

				if (!force_ignore_zocs && t.movement_left > 0
						&& enemy_zoc(units, teams, locs[i], viewing_team, u.side(), see_all)
						&& !u.get_ability_bool("skirmisher", locs[i])) {
					t.movement_left = 0;
				}
			}

			++nb_dest;
			int x = locs[i].x;
			if (x < xmin) xmin = x;
			if (xmax < x) xmax = x;
			int y = locs[i].y;
			if (y < ymin) ymin = y;
			if (ymax < y) ymax = y;

			bool in_list = next.in == search_counter + 1;
			t.in = search_counter + 1;
			next = t;

			// if already in the priority queue then we just update it, else push it.
			if (in_list) { // never happen see next_visited above
				std::push_heap(pq.begin(), std::find(pq.begin(), pq.end(), index(locs[i])) + 1, node_comp);
			} else {
				pq.push_back(index(locs[i]));
				std::push_heap(pq.begin(), pq.end(), node_comp);
			}
		}
	}

	// Build the routes for every map_location that we reached.
	// The ordering must be compatible with map_location::operator<.
	destinations.reserve(nb_dest);
	for (int x = xmin; x <= xmax; ++x) {
		for (int y = ymin; y <= ymax; ++y)
		{
			const node &n = nodes[index(map_location(x, y))];
			if (n.in - search_counter > 1u) continue;
			paths::step s =
				{ n.curr, n.prev, n.movement_left + n.turns_left * total_movement };
			destinations.push_back(s);
		}
	}
}
surface getMinimap(int w, int h, const gamemap &map, const team *vw)
{
	const int scale = 8;

	DBG_DP << "creating minimap " << int(map.w()*scale*0.75) << "," << int(map.h()*scale) << "\n";

	const size_t map_width = map.w()*scale*3/4;
	const size_t map_height = map.h()*scale;
	if(map_width == 0 || map_height == 0) {
		return surface(NULL);
	}

	surface minimap(create_neutral_surface(map_width, map_height));
	if(minimap == NULL)
		return surface(NULL);

	typedef mini_terrain_cache_map cache_map;
	cache_map *normal_cache = &mini_terrain_cache;
	cache_map *fog_cache = &mini_fogged_terrain_cache;

	for(int y = 0; y != map.total_height(); ++y) {
		for(int x = 0; x != map.total_width(); ++x) {

			surface surf(NULL);

			const map_location loc(x,y);
			if(map.on_board(loc)) {
				const bool shrouded = vw != NULL && vw->shrouded(loc);
				// shrouded hex are not considered fogged (no need to fog a black image)
				const bool fogged = vw != NULL && !shrouded && vw->fogged(loc);
				const t_translation::t_terrain terrain = shrouded ?
					t_translation::VOID_TERRAIN : map[loc];

				bool need_fogging = false;

				cache_map* cache = fogged ? fog_cache : normal_cache;
				cache_map::iterator i = cache->find(terrain);

				if (fogged && i == cache->end()) {
					// we don't have the fogged version in cache
					// try the normal cache and ask fogging the image
					cache = normal_cache;
					i = cache->find(terrain);
					need_fogging = true;
				}

				if(i == cache->end()) {
					surface tile(get_image("terrain/" + map.get_terrain_info(terrain).minimap_image() + ".png",image::HEXED));

					if(tile == 0) {
						utils::string_map symbols;
						symbols["terrain"] = t_translation::write_terrain_code(terrain);
						const std::string msg =
							vgettext("Could not get image for terrain: $terrain.", symbols);
						VALIDATE(false, msg);
					}

					//Compose images of base and overlay if neccessary
					if(map.get_terrain_info(terrain).is_combined()) {
						surface overlay(get_image("terrain/" + map.get_terrain_info(terrain).minimap_image_overlay() + ".png", image::HEXED));
						if(overlay != 0 && overlay != tile) {
							surface combined = create_compatible_surface(tile, tile->w, tile->h);
							SDL_Rect r;
							r.x = 0;
							r.y = 0;
							SDL_BlitSurface(tile, NULL, combined, &r);
							r.x = std::max(0, (tile->w - overlay->w)/2);
							r.y = std::max(0, (tile->h - overlay->h)/2);
                            if ((overlay->flags & SDL_RLEACCEL) == 0) {
                                blit_surface(overlay, NULL, combined, &r);
                            } else {
                                WRN_DP << map.get_terrain_info(terrain).minimap_image_overlay() << ".png overlay is RLE-encoded, creating a neutral surface\n";
                                surface overlay_neutral = make_neutral_surface(overlay);
							    blit_surface(overlay_neutral, NULL, combined, &r);
                            }
							tile = combined;
						}

					}

					surf = surface(scale_surface_blended(tile,scale,scale));

					VALIDATE(surf != NULL, _("Error creating or aquiring an image."));

					i = normal_cache->insert(cache_map::value_type(terrain,surf)).first;
				}

				surf = i->second;

				if (need_fogging) {
					surf = surface(adjust_surface_colour(surf,-50,-50,-50));
					fog_cache->insert(cache_map::value_type(terrain,surf));
				}

				VALIDATE(surf != NULL, _("Error creating or aquiring an image."));

				// we need a balanced shift up and down of the hexes.
				// if not, only the bottom half-hexes are clipped
				// and it looks asymmetrical.

				// also do 1-pixel shift because the scaling
				// function seems to do it with its rounding
				SDL_Rect maprect = {x * scale*3/4 - 1,
					y*scale + scale/4 * (is_odd(x) ? 1 : -1) - 1,
					0, 0};
				SDL_BlitSurface(surf, NULL, minimap, &maprect);
			}
		}
	}

	double wratio = w*1.0 / minimap->w;
	double hratio = h*1.0 / minimap->h;
	double ratio = std::min<double>(wratio, hratio);

	minimap = scale_surface(minimap,
		static_cast<int>(minimap->w * ratio), static_cast<int>(minimap->h * ratio));

	DBG_DP << "done generating minimap\n";

	return minimap;
}
예제 #24
0
void attack_analysis::analyze(const gamemap& map, unit_map& units,
				std::map<std::pair<const unit*, const unit_type*>, battle_context*>& unit_stats_cache,
				const std::vector<std::pair<unit*, int> >& units2,
				const std::multimap<map_location, int>& srcdst2, const std::multimap<int, map_location>& dstsrc2,
				std::map<unit*, std::pair<map_location, unit*>* >& reside_cache,
				double aggression)
{
	target_value = target->cost();
	target_value += 0.5 * (double(target->experience()) / double(target->max_experience())) * target_value;
	target_starting_damage = target->max_hitpoints() - target->hitpoints();

	VALIDATE(!movements.empty(), _("ai::attack_analisis::analyze, movements is empty."));

	std::pair<std::pair<unit*, int>, map_location>& m = movements.back();
	map_location orig_loc = m.first.first->get_location();

	std::pair<map_location, unit*>* up;
	if (m.first.second < 0) {
		// We fix up units map to reflect what this would look like.
		if (m.second == orig_loc) {
			up = units.get_cookie(m.second, !m.first.first->base());
		} else {
			up = units.extract(orig_loc);
			up->first = m.second;
			units.place(up);
		}
	} else {
		// units.add(m.second, m.first.first);
		std::map<unit*, std::pair<map_location, unit*>* >::iterator cache_itor = reside_cache.find(m.first.first);
		if (cache_itor == reside_cache.end()) {
			up = new std::pair<map_location, unit*>(m.second, m.first.first);
			reside_cache[m.first.first] = up;
		} else {
			up = cache_itor->second;
		}
		up->first = m.second;
		units.place(up);
	}

	unit_map::node* base = units.get_cookie(m.second, false);
	bool on_wall = base && base->second->wall();

		int att_weapon = -1, def_weapon = -1;
		bool from_cache = false;
		battle_context *bc;
		const unit* src_ptr = up->second;

		// This cache is only about 99% correct, but speeds up evaluation by about 1000 times.
		// We recalculate when we actually attack.
		const unit_type* src_type;
		if (!src_ptr->packed()) {
			src_type = src_ptr->type();
		} else {
			src_type = unit_types.find(src_ptr->packee_type_id());
		}

		std::map<std::pair<const unit*, const unit_type*>, battle_context*>::iterator usc;
		usc = unit_stats_cache.find(std::pair<const unit*, const unit_type*>(target, src_type));
		// Just check this attack is valid for this attacking unit (may be modified)
		if (usc != unit_stats_cache.end() && usc->second->get_attacker_stats().attack_num < static_cast<int>(src_ptr->attacks().size())) {
			from_cache = true;
			bc = usc->second;
		} else {
			VALIDATE(false, _("ai::attack_analisis::analyze, cannot find unit_type pair in usc."));
		}

		// because save battle_context into usc direct, don't support prev_def.
		const combatant &att = bc->get_attacker_combatant(NULL);
		const combatant &def = bc->get_defender_combatant(NULL);

		// Note we didn't fight at all if defender is already dead.
		double prob_fought = (1.0 - prob_dead_already);

		// @todo 1.8 add combatant.prob_killed
		double prob_killed = def.dead_ - prob_dead_already;
		prob_dead_already = def.dead_;

		double prob_died = att.dead_;
		double prob_survived = (1.0 - prob_died) * prob_fought;

		double cost = up->second->cost();
		const bool on_village = map.is_village(m.second);
		// Up to double the value of a unit based on experience
		cost += 0.5 * (double(up->second->experience())/double(up->second->max_experience())) * cost;
		// We should encourage multi-phase attack.
		cost /= movements.size();

		resources_used += cost;
		avg_losses += cost * prob_died;

		// add half of cost for poisoned unit so it might get chance to heal
		avg_losses += cost * up->second->get_state(unit::STATE_POISONED) /2;

		// Double reward to emphasize getting onto villages if they survive.
		if (on_village) {
			avg_damage_taken -= game_config::poison_amount*2 * prob_survived;
		}

		double quality = (double(bc->get_defender_stats().chance_to_hit)/100.0) * cost * (on_village ? 0.5 : 1.0);
		if (on_wall) {
			quality *= 0.2;
		}
		terrain_quality += quality;

		double advance_prob = 0.0;
		// The reward for advancing a unit is to get a 'negative' loss of that unit
		if (!up->second->advances_to().empty()) {
			int xp_for_advance = up->second->max_experience() - up->second->experience();
			int kill_xp, fight_xp;

			// See bug #6272... in some cases, unit already has got enough xp to advance,
			// but hasn't (bug elsewhere?).  Can cause divide by zero.
			if (xp_for_advance <= 0)
				xp_for_advance = 1;

			fight_xp = target->level();
			kill_xp = fight_xp ? fight_xp * game_config::kill_experience :
				game_config::kill_experience / 2;

			if (fight_xp >= xp_for_advance) {
				advance_prob = prob_fought;
				avg_losses -= up->second->cost() * prob_fought;
			} else if (kill_xp >= xp_for_advance) {
				advance_prob = prob_killed;
				avg_losses -= up->second->cost() * prob_killed;
				// The reward for getting a unit closer to advancement
				// (if it didn't advance) is to get the proportion of
				// remaining experience needed, and multiply it by
				// a quarter of the unit cost.
				// This will cause the AI to heavily favor
				// getting xp for close-to-advance units.
				avg_losses -= up->second->cost() * 0.25 *
					fight_xp * (prob_fought - prob_killed)
					/ xp_for_advance;
			} else {
				avg_losses -= up->second->cost() * 0.25 *
					(kill_xp * prob_killed + fight_xp * (prob_fought - prob_killed))
					/ xp_for_advance;
			}

			// The reward for killing with a unit that plagues
			// is to get a 'negative' loss of that unit.
			if (bc->get_attacker_stats().plagues) {
				avg_losses -= prob_killed * up->second->cost();
			}
		}

		// If we didn't advance, we took this damage.
		avg_damage_taken += (up->second->hitpoints() - att.average_hp()) * (1.0 - advance_prob);

		//
		// @todo 1.8: attack_prediction.cpp should understand advancement
		// directly.  For each level of attacker def gets 1 xp or
		// kill_experience.
		//
		int fight_xp = up->second->level();
		int kill_xp = fight_xp ? fight_xp * game_config::kill_experience :
				game_config::kill_experience / 2;
		def_avg_experience += fight_xp * (1.0 - att.dead_) + kill_xp * att.dead_;
	

	if (!target->advances_to().empty() && def_avg_experience >= target->max_experience() - target->experience()) {
		// It's likely to advance: only if we can kill with first blow.
		if (movements.size() == 1) {
			chance_to_kill = def.dead_;
		}
		// Negative average damage (it will advance).
		avg_damage_inflicted += target->hitpoints() - target->max_hitpoints();
	} else {
		chance_to_kill += def.dead_;
		avg_damage_inflicted += target->hitpoints() - def.average_hp(map.gives_healing(target->get_location()));
	}

	// Restore the units to their original positions.
	if (m.first.second < 0) {
		if (m.second != orig_loc) {
			units.move(m.second, orig_loc);
		}
	} else {
		units.extract(m.second);
		// place() will update loc_ of up->second, it is time to be back to orig_loc.
		up->second->set_location(orig_loc);
		// delete up;
	}
}
예제 #25
0
bool editor_map::same_size_as(const gamemap& other) const
{
	return h() == other.h()
		&& w() == other.w();
}
예제 #26
0
surface getMinimap(int w, int h, const gamemap &map, const team *vw)
{
	const int scale = 8;

	DBG_DP << "creating minimap " << int(map.w()*scale*0.75) << "," << map.h()*scale << "\n";

	const size_t map_width = map.w()*scale*3/4;
	const size_t map_height = map.h()*scale;
	if(map_width == 0 || map_height == 0) {
		return surface(NULL);
	}

	surface minimap(create_neutral_surface(map_width, map_height));
	if(minimap == NULL)
		return surface(NULL);

	typedef mini_terrain_cache_map cache_map;
	cache_map *normal_cache = &mini_terrain_cache;
	cache_map *fog_cache = &mini_fogged_terrain_cache;

	for(int y = 0; y != map.total_height(); ++y) {
		for(int x = 0; x != map.total_width(); ++x) {

			surface surf(NULL);

			const map_location loc(x,y);
			if(map.on_board(loc)) {

				const bool shrouded = (vw != NULL && vw->shrouded(loc));
				// shrouded hex are not considered fogged (no need to fog a black image)
				const bool fogged = (vw != NULL && !shrouded && vw->fogged(loc));
				const t_translation::t_terrain terrain = shrouded ?
						t_translation::VOID_TERRAIN : map[loc];
				const terrain_type& terrain_info = map.get_terrain_info(terrain);

				bool need_fogging = false;

				cache_map* cache = fogged ? fog_cache : normal_cache;
				cache_map::iterator i = cache->find(terrain);

				if (fogged && i == cache->end()) {
					// we don't have the fogged version in cache
					// try the normal cache and ask fogging the image
					cache = normal_cache;
					i = cache->find(terrain);
					need_fogging = true;
				}

				if(i == cache->end()) {
					std::string base_file =
							"terrain/" + terrain_info.minimap_image() + ".png";
					surface tile = get_image(base_file,image::HEXED);

					//Compose images of base and overlay if necessary
					// NOTE we also skip overlay when base is missing (to avoid hiding the error)
					if(tile != NULL && map.get_terrain_info(terrain).is_combined()) {
						std::string overlay_file =
								"terrain/" + terrain_info.minimap_image_overlay() + ".png";
						surface overlay = get_image(overlay_file,image::HEXED);

						if(overlay != NULL && overlay != tile) {
							surface combined = create_neutral_surface(tile->w, tile->h);
							SDL_Rect r = create_rect(0,0,0,0);
							sdl_blit(tile, NULL, combined, &r);
							r.x = std::max(0, (tile->w - overlay->w)/2);
							r.y = std::max(0, (tile->h - overlay->h)/2);
							//blit_surface needs neutral surface
							surface overlay_neutral = make_neutral_surface(overlay);
							blit_surface(overlay_neutral, NULL, combined, &r);
							tile = combined;
						}
					}

					surf = scale_surface_sharp(tile, scale, scale);

					i = normal_cache->insert(cache_map::value_type(terrain,surf)).first;
				}

				surf = i->second;

				if (need_fogging) {
					surf = adjust_surface_color(surf,-50,-50,-50);
					fog_cache->insert(cache_map::value_type(terrain,surf));
				}

				// we need a balanced shift up and down of the hexes.
				// if not, only the bottom half-hexes are clipped
				// and it looks asymmetrical.

				// also do 1-pixel shift because the scaling
				// function seems to do it with its rounding
				SDL_Rect maprect = create_rect(
						  x * scale * 3 / 4 - 1
						, y * scale + scale / 4 * (is_odd(x) ? 1 : -1) - 1
						, 0
						, 0);

				if(surf != NULL)
					sdl_blit(surf, NULL, minimap, &maprect);
			}
		}
	}

	double wratio = w*1.0 / minimap->w;
	double hratio = h*1.0 / minimap->h;
	double ratio = std::min<double>(wratio, hratio);

	minimap = scale_surface_sharp(minimap,
		static_cast<int>(minimap->w * ratio), static_cast<int>(minimap->h * ratio));

	DBG_DP << "done generating minimap\n";

	return minimap;
}
예제 #27
0
파일: attack.cpp 프로젝트: sunny975/wesnoth
void attack_analysis::analyze(const gamemap& map, unit_map& units,
                              const readonly_context& ai_obj,
                              const move_map& dstsrc, const move_map& srcdst,
                              const move_map& enemy_dstsrc, double aggression)
{
	const unit_map::const_iterator defend_it = units.find(target);
	assert(defend_it != units.end());

	// See if the target is a threat to our leader or an ally's leader.
	map_location adj[6];
	get_adjacent_tiles(target,adj);
	size_t tile;
	for(tile = 0; tile != 6; ++tile) {
		const unit_map::const_iterator leader = units.find(adj[tile]);
		if(leader != units.end() && leader->can_recruit() && !ai_obj.current_team().is_enemy(leader->side())) {
			break;
		}
	}

	leader_threat = (tile != 6);
	uses_leader = false;

	target_value = defend_it->cost();
	target_value += (double(defend_it->experience())/
	                 double(defend_it->max_experience()))*target_value;
	target_starting_damage = defend_it->max_hitpoints() -
	                         defend_it->hitpoints();

	// Calculate the 'alternative_terrain_quality' -- the best possible defensive values
	// the attacking units could hope to achieve if they didn't attack and moved somewhere.
	// This is used for comparative purposes, to see just how vulnerable the AI is
	// making itself.
	alternative_terrain_quality = 0.0;
	double cost_sum = 0.0;
	for(size_t i = 0; i != movements.size(); ++i) {
		const unit_map::const_iterator att = units.find(movements[i].first);
		const double cost = att->cost();
		cost_sum += cost;
		alternative_terrain_quality += cost*ai_obj.best_defensive_position(movements[i].first,dstsrc,srcdst,enemy_dstsrc).chance_to_hit;
	}
	alternative_terrain_quality /= cost_sum*100;

	avg_damage_inflicted = 0.0;
	avg_damage_taken = 0.0;
	resources_used = 0.0;
	terrain_quality = 0.0;
	avg_losses = 0.0;
	chance_to_kill = 0.0;

	double def_avg_experience = 0.0;
	double first_chance_kill = 0.0;

	double prob_dead_already = 0.0;
	assert(!movements.empty());
	std::vector<std::pair<map_location,map_location> >::const_iterator m;

	battle_context *prev_bc = NULL;
	const combatant *prev_def = NULL;

	for (m = movements.begin(); m != movements.end(); ++m) {
		// We fix up units map to reflect what this would look like.
		unit_ptr up = units.extract(m->first);
		up->set_location(m->second);
		units.insert(up);
		double m_aggression = aggression;

		if (up->can_recruit()) {
			uses_leader = true;
			// FIXME: suokko's r29531 omitted this line
			leader_threat = false;
			m_aggression = ai_obj.get_leader_aggression();
		}

		bool from_cache = false;
		battle_context *bc;

		// This cache is only about 99% correct, but speeds up evaluation by about 1000 times.
		// We recalculate when we actually attack.
		const readonly_context::unit_stats_cache_t::key_type cache_key = std::make_pair(target, &up->type());
		const readonly_context::unit_stats_cache_t::iterator usc = ai_obj.unit_stats_cache().find(cache_key);
		// Just check this attack is valid for this attacking unit (may be modified)
		if (usc != ai_obj.unit_stats_cache().end() &&
				usc->second.first.attack_num <
				static_cast<int>(up->attacks().size())) {

			from_cache = true;
			bc = new battle_context(usc->second.first, usc->second.second);
		} else {
			bc = new battle_context(units, m->second, target, -1, -1, m_aggression, prev_def);
		}
		const combatant &att = bc->get_attacker_combatant(prev_def);
		const combatant &def = bc->get_defender_combatant(prev_def);

		delete prev_bc;
		prev_bc = bc;
		prev_def = &bc->get_defender_combatant(prev_def);

		if ( !from_cache ) {
			ai_obj.unit_stats_cache().insert(
				std::make_pair(cache_key, std::make_pair(bc->get_attacker_stats(),
				                                         bc->get_defender_stats())));
		}

		// Note we didn't fight at all if defender is already dead.
		double prob_fought = (1.0 - prob_dead_already);

		/** @todo 1.9 add combatant.prob_killed */
		double prob_killed = def.hp_dist[0] - prob_dead_already;
		prob_dead_already = def.hp_dist[0];

		double prob_died = att.hp_dist[0];
		double prob_survived = (1.0 - prob_died) * prob_fought;

		double cost = up->cost();
		const bool on_village = map.is_village(m->second);
		// Up to double the value of a unit based on experience
		cost += (double(up->experience()) / up->max_experience())*cost;
		resources_used += cost;
		avg_losses += cost * prob_died;

		// add half of cost for poisoned unit so it might get chance to heal
		avg_losses += cost * up->get_state(unit::STATE_POISONED) /2;

		if (!bc->get_defender_stats().is_poisoned) {
			avg_damage_inflicted += game_config::poison_amount * 2 * bc->get_defender_combatant().poisoned * (1 - prob_killed);
		}

		// Double reward to emphasize getting onto villages if they survive.
		if (on_village) {
			avg_damage_taken -= game_config::poison_amount*2 * prob_survived;
		}

		terrain_quality += (double(bc->get_defender_stats().chance_to_hit)/100.0)*cost * (on_village ? 0.5 : 1.0);

		double advance_prob = 0.0;
		// The reward for advancing a unit is to get a 'negative' loss of that unit
		if (!up->advances_to().empty()) {
			int xp_for_advance = up->max_experience() - up->experience();

			// See bug #6272... in some cases, unit already has got enough xp to advance,
			// but hasn't (bug elsewhere?).  Can cause divide by zero.
			if (xp_for_advance <= 0)
				xp_for_advance = 1;

			int fight_xp = defend_it->level();
			int kill_xp = game_config::kill_xp(fight_xp);

			if (fight_xp >= xp_for_advance) {
				advance_prob = prob_fought;
				avg_losses -= up->cost() * prob_fought;
			} else if (kill_xp >= xp_for_advance) {
				advance_prob = prob_killed;
				avg_losses -= up->cost() * prob_killed;
				// The reward for getting a unit closer to advancement
				// (if it didn't advance) is to get the proportion of
				// remaining experience needed, and multiply it by
				// a quarter of the unit cost.
				// This will cause the AI to heavily favor
				// getting xp for close-to-advance units.
				avg_losses -= up->cost() * 0.25 *
					fight_xp * (prob_fought - prob_killed)
					/ xp_for_advance;
			} else {
				avg_losses -= up->cost() * 0.25 *
					(kill_xp * prob_killed + fight_xp * (prob_fought - prob_killed))
					/ xp_for_advance;
			}

			// The reward for killing with a unit that plagues
			// is to get a 'negative' loss of that unit.
			if (bc->get_attacker_stats().plagues) {
				avg_losses -= prob_killed * up->cost();
			}
		}

		// If we didn't advance, we took this damage.
		avg_damage_taken += (up->hitpoints() - att.average_hp()) * (1.0 - advance_prob);

		/**
		 * @todo 1.9: attack_prediction.cpp should understand advancement
		 * directly.  For each level of attacker def gets 1 xp or
		 * kill_experience.
		 */
		int fight_xp = up->level();
		int kill_xp = game_config::kill_xp(fight_xp);
		def_avg_experience += fight_xp * (1.0 - att.hp_dist[0]) + kill_xp * att.hp_dist[0];
		if (m == movements.begin()) {
			first_chance_kill = def.hp_dist[0];
		}
	}

	if (!defend_it->advances_to().empty() &&
		def_avg_experience >= defend_it->max_experience() - defend_it->experience()) {
		// It's likely to advance: only if we can kill with first blow.
		chance_to_kill = first_chance_kill;
		// Negative average damage (it will advance).
		avg_damage_inflicted += defend_it->hitpoints() - defend_it->max_hitpoints();
	} else {
		chance_to_kill = prev_def->hp_dist[0];
		avg_damage_inflicted += defend_it->hitpoints() - prev_def->average_hp(map.gives_healing(defend_it->get_location()));
	}

	delete prev_bc;
	terrain_quality /= resources_used;

	// Restore the units to their original positions.
	for (m = movements.begin(); m != movements.end(); ++m) {
		units.move(m->second, m->first);
	}
}
	/**
	 * Create a tile info -- the constructor grabs required data from the map
	 */
	tile_info(const gamemap& map, const map_location& offset)
	: offset(offset), terrain(map.get_terrain(offset))
	{
	}
예제 #29
0
파일: map.cpp 프로젝트: niegenug/wesnoth
void gamemap::overlay(const gamemap& m, const config& rules_cfg, int xpos, int ypos, bool border)
{
	const config::const_child_itors &rules = rules_cfg.child_range("rule");
	int actual_border = (m.border_size() == border_size()) && border ? border_size() : 0;

	const int xstart = std::max<int>(-actual_border, -xpos - actual_border);
	const int ystart = std::max<int>(-actual_border, -ypos - actual_border - ((xpos & 1) ? 1 : 0));
	const int xend = std::min<int>(m.w() + actual_border, w() + actual_border - xpos);
	const int yend = std::min<int>(m.h() + actual_border, h() + actual_border - ypos);
	for(int x1 = xstart; x1 < xend; ++x1) {
		for(int y1 = ystart; y1 < yend; ++y1) {
			const int x2 = x1 + xpos;
			const int y2 = y1 + ypos +
				((xpos & 1) && (x1 & 1) ? 1 : 0);

			const t_translation::t_terrain t = m[x1][y1 + m.border_size_];
			const t_translation::t_terrain current = (*this)[x2][y2 + border_size_];

			if(t == t_translation::FOGGED || t == t_translation::VOID_TERRAIN) {
				continue;
			}

			// See if there is a matching rule
			config::const_child_iterator rule = rules.first;
			for( ; rule != rules.second; ++rule)
			{
				static const std::string src_key = "old", dst_key = "new";
				const config &cfg = *rule;
				const t_translation::t_list& src = t_translation::read_list(cfg[src_key]);

				if(!src.empty() && t_translation::terrain_matches(current, src) == false) {
					continue;
				}

				const t_translation::t_list& dst = t_translation::read_list(cfg[dst_key]);

				if(!dst.empty() && t_translation::terrain_matches(t, dst) == false) {
					continue;
				}

				break;
			}


			if (rule != rules.second)
			{
				const config &cfg = *rule;
				const t_translation::t_list& terrain = t_translation::read_list(cfg["terrain"]);

				terrain_type_data::tmerge_mode mode = terrain_type_data::BOTH;
				if (cfg["layer"] == "base") {
					mode = terrain_type_data::BASE;
				}
				else if (cfg["layer"] == "overlay") {
					mode = terrain_type_data::OVERLAY;
				}

				t_translation::t_terrain new_terrain = t;
				if(!terrain.empty()) {
					new_terrain = terrain[0];
				}

				if (!cfg["use_old"].to_bool()) {
					set_terrain(map_location(x2, y2), new_terrain, mode, cfg["replace_if_failed"].to_bool());
				}

			} else {
				set_terrain(map_location(x2,y2),t);
			}
		}
	}

	for(const map_location* pos = m.startingPositions_;
			pos != m.startingPositions_ + sizeof(m.startingPositions_)/sizeof(*m.startingPositions_);
			++pos) {

		if(pos->valid()) {
			startingPositions_[pos - m.startingPositions_] = *pos;
		}
	}
}