/** * EntityUtil.h * Various utility functions */ #ifndef _ENTITY_UTIL_H_ #define _ENTITY_UTIL_H_ //------------------------------------------------------------------------------------------ enum WhoType { ANYONE, ONLY_FRIENDS, ONLY_ENEMIES }; //-------------------------------------------------------------------------------------------------------------- /** * Iterate over all entities in the game, invoking functor on each. * If functor returns false, stop iteration and return false. */ template < typename Functor > bool ForEachEntity( Functor &func ) { CBaseEntity *entity = gEntList.FirstEnt(); while ( entity ) { if ( func( entity ) == false ) return false; entity = gEntList.NextEnt( entity ); } return true; } //-------------------------------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------------------------------- #ifdef GAME_DLL class FindInViewConeFunctor { public: FindInViewConeFunctor( CBasePlayer *me, WhoType who, float tolerance, CBaseEntity *ignore = NULL ) { m_me = me; m_who = who; m_tolerance = tolerance; m_target = NULL; m_range = 9999999.9f; m_ignore = ignore; me->EyeVectors(&m_dir, NULL, NULL); m_origin = me->GetAbsOrigin() + me->GetViewOffset(); } bool operator() ( CBasePlayer *player ) { if (player != m_me && player != m_ignore && player->IsAlive()) { if (m_who == ONLY_FRIENDS && m_me->GetTeamNumber() != player->GetTeamNumber() ) return true; if (m_who == ONLY_ENEMIES && m_me->GetTeamNumber() == player->GetTeamNumber() ) return true; Vector to = player->WorldSpaceCenter() - m_origin; float range = to.NormalizeInPlace(); if (DotProduct( to, m_dir ) > m_tolerance && range < m_range) { if ( m_me->IsLineOfSightClear( player ) ) { m_target = player; m_range = range; } } } return true; } CBasePlayer *m_me; WhoType m_who; float m_tolerance; CBaseEntity *m_ignore; Vector m_origin; Vector m_dir; CBasePlayer *m_target; float m_range; }; /** * Find the closest player within the given view cone */ inline CBasePlayer *GetClosestPlayerInViewCone( CBasePlayer *me, WhoType who, float tolerance = 0.95f, float *range = NULL, CBaseEntity *ignore = NULL ) { // choke the victim we are pointing at FindInViewConeFunctor checkCone( me, who, tolerance, ignore ); ForEachPlayer( checkCone ); if (range) *range = checkCone.m_range; return checkCone.m_target; } #endif //-------------------------------------------------------------------------------------------------------------- /** * Find player closest to ray. * Return perpendicular distance to ray in 'offset' if it is non-NULL. */ extern CBasePlayer *GetPlayerClosestToRay( CBasePlayer *me, WhoType who, const Vector &start, const Vector &end, float *offset = NULL ); //-------------------------------------------------------------------------------------------------------------- /** * Compute the closest point on the ray to 'pos' and return it in 'pointOnRay'. * If point projects beyond the ends of the ray, return false - but clamp 'pointOnRay' to the correct endpoint. */ inline bool ClosestPointOnRay( const Vector &pos, const Vector &rayStart, const Vector &rayEnd, Vector *pointOnRay ) { Vector to = pos - rayStart; Vector dir = rayEnd - rayStart; float length = dir.NormalizeInPlace(); float rangeAlong = DotProduct( dir, to ); if (rangeAlong < 0.0f) { // off start point *pointOnRay = rayStart; return false; } else if (rangeAlong > length) { // off end point *pointOnRay = rayEnd; return false; } else // within ray bounds { Vector onRay = rayStart + rangeAlong * dir; *pointOnRay = onRay; return true; } } //------------------------------------------------------------------------------------------ /** * Functor to find the player closest to a ray. * For use with ForEachPlayerNearRay(). */ class ClosestToRayFunctor { public: ClosestToRayFunctor( CBasePlayer *ignore, WhoType who = ANYONE ) { m_ignore = ignore; m_who = who; m_closestPlayer = NULL; m_closestPlayerRange = 999999999.9f; } bool operator() ( CBasePlayer *player, float rangeToRay ) { if (player == m_ignore) return true; if (!player->IsAlive()) return true; if (m_who == ONLY_FRIENDS && m_ignore->GetTeamNumber() != player->GetTeamNumber() ) return true; if (m_who == ONLY_ENEMIES && m_ignore->GetTeamNumber() == player->GetTeamNumber() ) return true; // keep the player closest to the ray if (rangeToRay < m_closestPlayerRange) { m_closestPlayerRange = rangeToRay; m_closestPlayer = player; } return true; } CBasePlayer *m_ignore; WhoType m_who; CBasePlayer *m_closestPlayer; float m_closestPlayerRange; }; //-------------------------------------------------------------------------------------------------------------- /** * Iterate over all players that are within range of the ray and invoke functor. * If functor returns false, stop iteration and return false. * @todo Check LOS to ray. */ template < typename Functor > bool ForEachPlayerNearRay( Functor &func, const Vector &start, const Vector &end, float maxRadius ) { for( int i=1; i<=gpGlobals->maxClients; ++i ) { CBasePlayer *player = static_cast( UTIL_PlayerByIndex( i ) ); if (player == NULL) continue; if (FNullEnt( player->edict() )) continue; if (!player->IsPlayer()) continue; if (!player->IsAlive()) continue; float range = DistanceToRay( player->WorldSpaceCenter(), start, end ); if (range < 0.0f || range > maxRadius) continue; if (func( player, range ) == false) return false; } return true; } //-------------------------------------------------------------------------------------------------------------- /** * Iterate over all players that are within range of the sphere and invoke functor. * If functor returns false, stop iteration and return false. * @todo Check LOS to ray. */ template < typename Functor > bool ForEachPlayerNearSphere( Functor &func, const Vector &origin, float radius ) { for( int i=1; i<=gpGlobals->maxClients; ++i ) { CBasePlayer *player = static_cast( UTIL_PlayerByIndex( i ) ); if (player == NULL) continue; if (FNullEnt( player->edict() )) continue; if (!player->IsPlayer()) continue; if (!player->IsAlive()) continue; Vector to = player->WorldSpaceCenter() - origin; float range = to.Length(); if (range > radius) continue; if (func( player, range ) == false) return false; } return true; } //-------------------------------------------------------------------------------------------------------------- /** * Reflect a vector. * Assumes 'unitNormal' is normalized. */ inline Vector VectorReflect( const Vector &in, const Vector &unitNormal ) { // compute the unit vector out of the plane of 'in' and 'unitNormal' Vector lat = CrossProduct( in, unitNormal ); Vector forward = CrossProduct( unitNormal, lat ); forward.NormalizeInPlace(); Vector forwardComponent = forward * DotProduct( in, forward ); Vector normalComponent = unitNormal * DotProduct( in, unitNormal ); return forwardComponent - normalComponent; } #endif // _ENTITY_UTIL_H_