text: ////////////////////////////////////////////////////////////////////////////
  //	Module 		: smart_cover.cpp
  //	Created 	: 16.08.2007
  //	Author		: Alexander Dudin
  //	Description : Smart cover class
  ////////////////////////////////////////////////////////////////////////////
  
  #include "pch_script.h"
  #include "smart_cover.h"
  #include "smart_cover_storage.h"
  #include "smart_cover_object.h"
  #include "ai_object_location.h"
  #include "smart_cover_action.h"
  #include "ai_space.h"
  #include "level_graph.h"
  #include "graph_engine.h"
  
  namespace hash_fixed_vertex_manager {
  	
  IC	u32 to_u32	(shared_str const &string)
  {
  	const str_value	*get = string._get();
  	return			(*(u32 const*)&get);
  }
  
  } // namespace hash_fixed_vertex_manager
  
  namespace smart_cover {
  	shared_str	transform_vertex(shared_str const &vertex_id, bool const &in);
  } // namespace smart_cover
  
  using smart_cover::cover;
  using smart_cover::description;
  using smart_cover::transform_vertex;
  
  cover::cover					(smart_cover::object const &object, DescriptionPtr description, bool const &is_combat_cover) : 
  	inherited		(object.Position(), object.ai_location().level_vertex_id()),
  	m_object		(object),
  	m_description	(description),
  	m_id			(m_object.cName()),
  	m_is_combat_cover (is_combat_cover)
  {
  	m_is_smart_cover			= 1;
  
  	CLevelGraph const			&graph = ai().level_graph();
  	m_vertices.resize			(loopholes().size());
  	Vertices::iterator			i = m_vertices.begin();
  	Loopholes::const_iterator	I = loopholes().begin();
  	Loopholes::const_iterator	E = loopholes().end();
  	for ( ; I != E; ++I, ++i) {
  		Fvector					position = this->fov_position(**I);
  		position.y				+= 2.0f;
  		u32						level_vertex_id = graph.vertex_id(position);
  		VERIFY2					(
  			graph.valid_vertex_id(level_vertex_id),
  			make_string(
  				"invalid vertex id: smart cover[%s], loophole [%s]",
  				m_object.cName().c_str(),
  				(*I)->id().c_str()
  			)
  		);
  		vertex					(**I, (*i).second);
  		const_cast<loophole*&>((*i).first) = *I;
  	}
  
  #ifdef DEBUG
  	check_loopholes_connectivity();
  #endif // DEBUG
  }
  
  cover::~cover					()
  {
  }
  
  void cover::vertex				(smart_cover::loophole const &loophole, smart_cover::loophole_data &loophole_data)
  {
  	CLevelGraph const			&graph = ai().level_graph();
  	Fvector						pos = fov_position(loophole);
  	pos.y						+= 2.0f;
  	loophole_data.m_level_vertex_id	= graph.vertex_id(pos);
  	VERIFY2						(
  		graph.valid_vertex_id(loophole_data.m_level_vertex_id),
  		make_string(
  			"invalid vertex id: smart cover[%s], loophole [%s]",
  			m_object.cName().c_str(),
  			loophole.id().c_str()
  		)
  	);
  	
  	typedef loophole::ActionList::const_iterator const_iterator;
  	const_iterator				I = loophole.actions().begin();
  	const_iterator				E = loophole.actions().end();
  	for ( ; I != E; ++I )
  		if((*I).second->movement()) {
  			Fvector				pos = position((*I).second->target_position());
  			pos.y				+= 2.0f;
  			u32					level_vertex_id = graph.vertex_id(pos);
  			VERIFY2				(
  				graph.valid_vertex_id(level_vertex_id),
  				make_string(
  					"invalid vertex id: loophole [%s]",
  					loophole.id().c_str()
  				)
  			);
  			loophole_data.m_action_vertices.push_back(std::make_pair((*I).first, level_vertex_id));
  		}
  }
  
  class id_predicate {
  	shared_str m_id;
  
  public:
  	IC	id_predicate(shared_str const &id) :
  		m_id	(id)
  	{
  	}
  
  	IC	bool	operator()	(cover::Vertex const &vertex) const
  	{
  		return		(m_id._get() == vertex.first->id()._get());
  	}
  
  	IC	bool	operator()	(smart_cover::loophole_data::Action const &action) const
  	{
  		return		(m_id._get() == action.first._get());
  	}
  };
  
  u32 const &cover::action_level_vertex_id(smart_cover::loophole const &loophole, shared_str const &action_id) const
  {
  	Vertices::const_iterator	found = std::find_if(
  												m_vertices.begin(),
  												m_vertices.end(),
  												id_predicate(loophole.id())
  										);
  	VERIFY						(found != m_vertices.end());
  	
  	loophole_data::ActionVertices::const_iterator	found2 = std::find_if(
  												found->second.m_action_vertices.begin(),
  												found->second.m_action_vertices.end(),
  												id_predicate(action_id)
  										);
  	VERIFY						(found2 != found->second.m_action_vertices.end());
  	VERIFY						(ai().level_graph().valid_vertex_id(found2->second));
  
  	return						(found2->second);
  }
  
  smart_cover::loophole *cover::best_loophole	(Fvector const &position, float &value, bool const &use_default_behaviour) const
  {
  	value						= flt_max;
  
  	loophole					*result = 0;
  	Loopholes::const_iterator	I = loopholes().begin();
  	Loopholes::const_iterator	E = loopholes().end();
  	for ( ; I != E; ++I) {
  		loophole				*loophole = (*I);
  		if (use_default_behaviour)
  			evaluate_loophole_for_default_usage	(position, loophole, result, value);
  		else
  			evaluate_loophole		(position, loophole, result, value);
  	}
  
  	return						(result);
  }
  
  void cover::evaluate_loophole				(Fvector const &position, smart_cover::loophole * &source, smart_cover::loophole * &result, float &value) const
  {
  	VERIFY						( source );
  	VERIFY2						( _valid(position), make_string("[%f][%f][%f]", VPUSH(position)) );
  
  	if (!source->usable())
  		return;
  
  	Fvector						fov_position = this->fov_position(*source);
  	VERIFY2						( _valid(fov_position), make_string("[%f][%f][%f]", VPUSH(fov_position)) );
  	if (fov_position.distance_to(position) > source->range())
  		return;
  
  	Fvector						direction = Fvector().sub(position, fov_position);
  	VERIFY2						( _valid(direction), make_string("[%f][%f][%f]", VPUSH(direction)) );
  	if (direction.magnitude() < 1.f)
  		return;
  
  	direction.normalize			();
  	float						cos_alpha = this->fov_direction(*source).dotproduct(direction);
  
  	float						alpha = _abs(acosf(cos_alpha));
  	if (alpha >= source->fov()/2)
  		return;
  
  	if (alpha >= value)
  		return;
  
  	value						= 2.f*alpha/source->fov();
  	result						= source;
  }
  
  void cover::evaluate_loophole_for_default_usage		(Fvector const &position, smart_cover::loophole * &source, smart_cover::loophole * &result, float &value) const
  {
  	VERIFY						(source);
  
  	if (!source->usable())
  		return;
  
  	Fvector						fov_position = this->fov_position(*source);
  	Fvector						direction = Fvector().sub(position, fov_position);
  	direction.normalize_safe	();
  	float						cos_alpha = this->fov_direction(*source).dotproduct(direction);
  	float						alpha = acosf(cos_alpha);
  	if (alpha >= value)
  		return;
  
  	value						= alpha;
  	result						= source;
  }
  
  struct loophole_predicate {
  	smart_cover::loophole const *m_loophole;
  
  	IC			loophole_predicate	(smart_cover::loophole const *loophole) :
  		m_loophole				(loophole)
  	{
  	}
  
  		IC	bool	operator()	(cover::Vertex const &vertex) const
  	{
  		return					(vertex.first == m_loophole);
  	}
  };
  
  u32 const &cover::level_vertex_id			(smart_cover::loophole const &loophole) const
  {
  	Vertices::const_iterator	I = std::find_if(m_vertices.begin(), m_vertices.end(), loophole_predicate(&loophole));
  	VERIFY						(I != m_vertices.end());
  	return						((*I).second.m_level_vertex_id);
  }
  
  #ifdef DEBUG
  bool cover::loophole_path					(shared_str const &source_raw, shared_str const &target_raw) const
  {
  	shared_str					source = transform_vertex(source_raw, true);
  	shared_str					target = transform_vertex(target_raw, false);
  
  	typedef GraphEngineSpace::CBaseParameters	CBaseParameters;
  	CBaseParameters				parameters(u32(-1),u32(-1),u32(-1));
  	bool						result = 
  		ai().graph_engine().search(
  			m_description->transitions(),
  			source,
  			target,
  			0,
  			parameters
  		);
  
  	VERIFY2						(
  		result,
  		make_string				(
  			"failde to find loophole path [%s]->[%s] in cover [%s]",
  			source.c_str(),
  			target.c_str(),
  			m_description->table_id().c_str()
  		)
  	);
  	return						(result);
  }
  
  void cover::check_loopholes_connectivity	() const
  {
  	VERIFY						(!loopholes().empty());
  
  	shared_str					enter = transform_vertex("", true);
  	shared_str					exit =  transform_vertex("", false);
  
  	Loopholes::const_iterator	I = loopholes().begin();
  	Loopholes::const_iterator	E = loopholes().end();
  	for ( ; I != E; ++I) {
  		shared_str const		&lhs = (*I)->id();
  		Loopholes::const_iterator	J = I + 1;
  		for ( ; J != E; ++J) {
  			shared_str const	&rhs = (*J)->id();
  			VERIFY2				(
  				loophole_path(lhs, rhs),
  				make_string(
  					"failed to find path [%s -> %s] in smart_cover [%s]",
  					lhs.c_str(),
  					rhs.c_str(),
  					m_description->table_id().c_str()
  				)
  			);
  			VERIFY2				(
  				loophole_path(rhs, lhs),
  				make_string(
  					"failed to find path [%s -> %s] in smart_cover [%s]",
  					rhs.c_str(),
  					lhs.c_str(),
  					m_description->table_id().c_str()
  				)
  			);
  		}
  
  		VERIFY2				(
  			loophole_path(lhs, exit),
  			make_string(
  				"failed to find path [%s -> %s] in smart_cover [%s]",
  				lhs.c_str(),
  				exit.c_str(),
  				m_description->table_id().c_str()
  			)
  		);
  		VERIFY2				(
  			loophole_path(enter, lhs),
  			make_string(
  				"failed to find path [%s -> %s] in smart_cover [%s]",
  				enter.c_str(),
  				lhs.c_str(),
  				m_description->table_id().c_str()
  			)
  		);
  	}
  }
  #endif // DEBUG
  
  bool cover::is_position_in_fov				(smart_cover::loophole const &source, Fvector const &position) const
  {
  	Fvector						fov_position = this->fov_position(source);
  	Fvector						direction = Fvector().sub(position, fov_position);
  	if (direction.magnitude() < 1.f)
  		return					(false);
  
  	direction.normalize			();
  	float						cos_alpha = this->fov_direction(source).dotproduct(direction);
  	float						alpha = acosf(cos_alpha);
  	if (alpha >= source.fov()/2)
  		return					(false);
  
  	return						(true);
  }
  
  bool cover::is_position_in_range			(smart_cover::loophole const &source, Fvector const &position) const
  {
  	Fvector						fov_position = this->fov_position(source);
  	if (fov_position.distance_to(position) > source.range())
  		return					(false);
  
  	return						(true);
  }
  
  bool cover::in_min_acceptable_range			(smart_cover::loophole const &source, Fvector const &position, float const &min_range) const
  {
  	Fvector						fov_position = this->fov_position(source);
  	if (fov_position.distance_to(position) < min_range)
  		return					(false);
  
  	return						(true);
  }