Esempio n. 1
0
		uint32_t install_(const std::string& fpath)
		{
			unit_map_cit cit = unit_map_.find(fpath);
			if(cit != unit_map_.end()) {  // find it !, To be not install.
				return 0;
			}

			auto tpath = strip_last_of_delimita_path(fpath);
			auto bpath = get_file_path(tpath);
			bpath += '/';

			unit_map_it it = unit_map_.find(bpath);
			if(it != unit_map_.end()) {
				unit_t& t = it->second;
				std::string name = get_file_name(tpath);
				if(fpath.back() == '/') name += '/';
				t.install_child(name);
			}

			uint32_t hnd = handle_set_.create();
			unit_t u;
			u.set_id(hnd);
			unit_map_.emplace(fpath, u);
			return hnd;
		}
Esempio n. 2
0
bool backstab_check(const map_location& attacker_loc,
                    const map_location& defender_loc,
                    const unit_map& units, const std::vector<team>& teams)
{
	const unit_map::const_iterator defender = units.find(defender_loc);
	if(defender == units.end()) return false; // No defender

	map_location adj[6];
	get_adjacent_tiles(defender_loc, adj);
	int i;
	for(i = 0; i != 6; ++i) {
		if(adj[i] == attacker_loc)
			break;
	}
	if(i >= 6) return false;  // Attack not from adjacent location

	const unit_map::const_iterator opp =
		units.find(adj[(i+3)%6]);
	if(opp == units.end()) return false; // No opposite unit
	if (opp->incapacitated()) return false;
	if (size_t(defender->side() - 1) >= teams.size() || size_t(opp->side() - 1) >= teams.size())
		return true; // If sides aren't valid teams, then they are enemies
	if (teams[defender->side() - 1].is_enemy(opp->side()))
		return true; // Defender and opposite are enemies
	return false; // Defender and opposite are friends
}
Esempio n. 3
0
battle_context::battle_context(const unit_map& units,
		const map_location& attacker_loc, const map_location& defender_loc,
		int attacker_weapon, int defender_weapon, double aggression,
		const combatant *prev_def, const unit* attacker_ptr) :
	attacker_stats_(nullptr), defender_stats_(nullptr), attacker_combatant_(nullptr),
	defender_combatant_(nullptr)
{
	const unit &attacker = attacker_ptr ? *attacker_ptr : *units.find(attacker_loc);
	const unit &defender = *units.find(defender_loc);
	const double harm_weight = 1.0 - aggression;

	if (attacker_weapon == -1 && attacker.attacks().size() == 1 && attacker.attacks()[0].attack_weight() > 0 )
		attacker_weapon = 0;

	if (attacker_weapon == -1) {
		attacker_weapon = choose_attacker_weapon(attacker, defender, units,
			attacker_loc, defender_loc,
				harm_weight, &defender_weapon, prev_def);
	} else if (defender_weapon == -1) {
		defender_weapon = choose_defender_weapon(attacker, defender, attacker_weapon,
			units, attacker_loc, defender_loc, prev_def);
	}

	// If those didn't have to generate statistics, do so now.
	if (!attacker_stats_) {
		const attack_type *adef = nullptr;
		const attack_type *ddef = nullptr;
		if (attacker_weapon >= 0) {
			VALIDATE(attacker_weapon < static_cast<int>(attacker.attacks().size()),
					_("An invalid attacker weapon got selected."));
			adef = &attacker.attacks()[attacker_weapon];
		}
		if (defender_weapon >= 0) {
			VALIDATE(defender_weapon < static_cast<int>(defender.attacks().size()),
					_("An invalid defender weapon got selected."));
			ddef = &defender.attacks()[defender_weapon];
		}
		assert(!defender_stats_ && !attacker_combatant_ && !defender_combatant_);
		attacker_stats_ = new battle_context_unit_stats(attacker, attacker_loc, attacker_weapon,
				true, defender, defender_loc, ddef, units);
		defender_stats_ = new battle_context_unit_stats(defender, defender_loc, defender_weapon, false,
				attacker, attacker_loc, adef, units);
	}

	// There have been various bugs where only one of these was set
	assert(attacker_stats_);
	assert(defender_stats_);
}
Esempio n. 4
0
		bool install_(const std::string& key, const T& value)
		{
			std::string fpath;
			if(!create_full_path(key, fpath)) {
				return false;
			}

			bool f = false;
			utils::strings ss = split_text(fpath, "/");
			std::string p;
			for(uint32_t i = 0; i < ss.size(); ++i) {
				p += '/';
				p += ss[i];
				unit_t u;
				u.value = value;
				u.set_id(serial_id_);
				std::pair<unit_map_it, bool> ret = unit_map_.insert(unit_pair(p, u));
				if(ret.second) {
					++serial_id_;
					auto prev = utils::get_file_path(p);
					if(p != prev) {
						unit_map_it it = unit_map_.find(prev);
						if(it != unit_map_.end()) {
							unit_t& t = it->second;
							t.install_child(utils::get_file_name(p));
						}
					}
					f = true;
				} else {
					f = false;
				}
			}
			return f;
		}
Esempio n. 5
0
void move::apply_temp_modifier(unit_map& unit_map)
{
	if (get_source_hex() == get_dest_hex())
		return; //zero-hex move, used by attack subclass

	// Safety: Make sure the old temporary_unit_mover (if any) is destroyed
	// before creating a new one.
	mover_.reset();

	//@todo: deal with multi-turn moves, which may for instance end their first turn
	// by capturing a village

	//@todo: we may need to change unit status here and change it back in remove_temp_modifier
	unit* unit;
	{
		unit_map::iterator unit_it = unit_map.find(get_source_hex());
		assert(unit_it != unit_map.end());
		unit = &*unit_it;
	}

	//Modify movement points
	DBG_WB <<"Move: Changing movement points for unit " << unit->name() << " [" << unit->id()
			<< "] from " << unit->movement_left() << " to "
			<< unit->movement_left() - movement_cost_ << ".\n";
	// Move the unit
	DBG_WB << "Move: Temporarily moving unit " << unit->name() << " [" << unit->id()
			<< "] from (" << get_source_hex() << ") to (" << get_dest_hex() <<")\n";
	mover_.reset(new temporary_unit_mover(unit_map, get_source_hex(), get_dest_hex(),
	                                      unit->movement_left() - movement_cost_));

	//Update status of fake unit (not undone by remove_temp_modifiers)
	//@todo this contradicts the name "temp_modifiers"
	fake_unit_->set_movement(unit->movement_left(), true);
}
Esempio n. 6
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;
}
Esempio n. 7
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;
}
Esempio n. 8
0
		//-----------------------------------------------------------------//
		utils::strings get_sub_directory(const std::string& root, bool full)
		{
			utils::strings list;

			auto fpath = create_full_path(root);
			if(fpath.empty()) {
				return list;
			}

			if(fpath.back() != '/') fpath += '/';

			unit_map_it it = unit_map_.find(fpath);
			if(it != unit_map_.end()) {
				const typename unit_t::childs& chs = it->second.get_childs();
				list.resize(chs.size());
				list.clear();
				for(const auto& s : chs) {
					if(full) {
						list.push_back(fpath + strip_last_of_delimita_path(s));
					} else {
						list.push_back(s);
					}
				}
			}
			return list;
		}
Esempio n. 9
0
		//-----------------------------------------------------------------//
		unit_map_its create_list(const std::string& root)
		{
			unit_map_its list;
			if(unit_map_.empty()) {
				list.clear();
				return list;
			}

			if(root.empty()) {
				list.resize(unit_map_.size());
				list.clear();
				for(unit_map_it it = unit_map_.begin(); it != unit_map_.end(); ++it) {
					list.push_back(it);
				}
			} else {
				auto fpath = create_full_path(root);
				if(fpath.empty()) {
					list.clear();
					return list;
				}

				unit_map_it it = unit_map_.find(fpath);
				if(it != unit_map_.end()) {
					const typename unit_t::childs& ch = it->second.get_childs();
					list.resize(ch.size());
					list.clear();
					for(const auto& s : ch) {
						auto path = create_full_path(s);
						if(!path.empty()) {
							it = unit_map_.find(path);
							if(it != unit_map_.end()) {
								list.push_back(it);
							}
						}
					}
				} else {
					list.clear();
				}
			}

			std::sort(list.begin(), list.end(), [] (unit_map_it l, unit_map_it r) {
				return l->first < r->first; }
			);

			return list;
		}
Esempio n. 10
0
std::pair<int, map_location> under_leadership(const unit_map& units, const map_location& loc)
{
	const unit_map::const_iterator un = units.find(loc);
	if(un == units.end()) {
		return {0, map_location::null_location()};
	}

	unit_ability_list abil = un->get_abilities("leadership");
	return abil.highest("value");
}
Esempio n. 11
0
		//-----------------------------------------------------------------//
		bool is_directory(const std::string& path) const
		{
			auto fpath = create_full_path(path);
			if(fpath.empty()) {
				return false;
			}

			if(fpath.back() != '/') fpath += '/';

			return is_directory(unit_map_.find(fpath));
		}
Esempio n. 12
0
		//-----------------------------------------------------------------//
		uint32_t find(const std::string& path) const {
			auto fpath = create_full_path(path);
			if(fpath.empty()) {
				return 0;
			}

			unit_map_cit cit = unit_map_.find(fpath);
			if(cit != unit_map_.end()) {
				return cit->second.get_id();
			} else {
				return 0;
			}
		}
Esempio n. 13
0
map_location under_leadership(const unit_map& units, const map_location& loc,
                              int* bonus)
{
	const unit_map::const_iterator un = units.find(loc);
	if(un == units.end()) {
		return map_location::null_location;
	}
	unit_ability_list abil = un->get_abilities("leadership");
	if(bonus) {
		*bonus = abil.highest("value").first;
	}
	return abil.highest("value").second;
}
Esempio n. 14
0
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();
}
Esempio n. 15
0
		uint32_t erase_(const std::string& fpath, handles& hnds)
		{
			unit_map_it it = unit_map_.find(fpath);
			if(it == unit_map_.end()) return 0;

			auto tpath = strip_last_of_delimita_path(fpath);
			auto bpath = get_file_path(tpath);
			bpath += '/';
			if(fpath.back() == '/') {  // is directory
				// erase sub directories
				unit_t::childs chs = it->second.get_childs();
				for(const auto& s : chs) {
//					std::cout << "Loop: " << (fpath + s) << std::endl;
					auto hnd = erase_(fpath + s, hnds);
					hnds.push_back(hnd);
				}
			}

			uint32_t hnd = 0;
			{
				unit_map_it it = unit_map_.find(fpath);
				if(it == unit_map_.end()) return 0;
				hnd = it->second.get_id();
				handle_set_.erase(hnd);
				unit_map_.erase(it);
			}

			{  // previous directory
				unit_map_it it = unit_map_.find(bpath);
				if(it != unit_map_.end()) {
					std::string s = get_file_name(tpath);
					if(fpath.back() == '/') s += '/';
					it->second.erase_child(s);
				}
			}
			return hnd;
		}
Esempio n. 16
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();
}
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();
}
Esempio n. 18
0
paths::paths(gamemap const &map, unit_map const &units,
		map_location const &loc, std::vector<team> const &teams,
		bool force_ignore_zoc, bool allow_teleport, const team &viewing_team,
		int additional_turns, bool see_all, bool ignore_units)
	: destinations()
{
	const unit_map::const_iterator i = units.find(loc);
	if(i == units.end()) {
		ERR_PF << "paths::paths() -- unit not found\n";
		return;
	}

	if(i->second.side() < 1 || i->second.side() > int(teams.size())) {
		return;
	}

	find_routes(map,units,i->second,loc,
		i->second.movement_left(), destinations, teams, force_ignore_zoc,
		allow_teleport,additional_turns,viewing_team,
		see_all, ignore_units);
}
Esempio n. 19
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);
				}
			}			
		}
	}
Esempio n. 20
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;
}
Esempio n. 21
0
 unit_map::iterator find_unit(const map_location & loc) {
     return units_.find(loc);
 }
Esempio n. 22
0
static void verify(const unit_map& units, const config& cfg) {
	std::stringstream errbuf;
	LOG_REPLAY << "verifying unit structure...\n";

	const size_t nunits = cfg["num_units"].to_size_t();
	if(nunits != units.size()) {
		errbuf << "SYNC VERIFICATION FAILED: number of units from data source differ: "
			   << nunits << " according to data source. " << units.size() << " locally\n";

		std::set<map_location> locs;
		for (const config &u : cfg.child_range("unit"))
		{
			const map_location loc(u);
			locs.insert(loc);

			if(units.count(loc) == 0) {
				errbuf << "data source says there is a unit at "
					   << loc << " but none found locally\n";
			}
		}

		for(unit_map::const_iterator j = units.begin(); j != units.end(); ++j) {
			if (locs.count(j->get_location()) == 0) {
				errbuf << "local unit at " << j->get_location()
					   << " but none in data source\n";
			}
		}
		replay::process_error(errbuf.str());
		errbuf.clear();
	}

	for (const config &un : cfg.child_range("unit"))
	{
		const map_location loc(un);
		const unit_map::const_iterator u = units.find(loc);
		if(u == units.end()) {
			errbuf << "SYNC VERIFICATION FAILED: data source says there is a '"
				   << un["type"] << "' (side " << un["side"] << ") at "
				   << loc << " but there is no local record of it\n";
			replay::process_error(errbuf.str());
			errbuf.clear();
		}

		config u_cfg;
		u->write(u_cfg);

		bool is_ok = true;
		static const std::string fields[] = {"type","hitpoints","experience","side",""};
		for(const std::string* str = fields; str->empty() == false; ++str) {
			if (u_cfg[*str] != un[*str]) {
				errbuf << "ERROR IN FIELD '" << *str << "' for unit at "
					   << loc << " data source: '" << un[*str]
					   << "' local: '" << u_cfg[*str] << "'\n";
				is_ok = false;
			}
		}

		if(!is_ok) {
			errbuf << "(SYNC VERIFICATION FAILED)\n";
			replay::process_error(errbuf.str());
			errbuf.clear();
		}
	}

	LOG_REPLAY << "verification passed\n";
}
Esempio n. 23
0
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);
	}
}