//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// // // Purpose: "Force-field" obstacle avoidance & steering // // @Note (toml 06-18-02): Currently only controls direction. Ultimately could // also incorporate body facing (yaw), speed, and translational/rotational // acceleration. // // $NoKeywords: $ //=============================================================================// #ifndef AI_MOVESOLVER_H #define AI_MOVESOLVER_H #if defined( _WIN32 ) #pragma once #endif #include "utlvector.h" #include "ai_obstacle_type.h" //----------------------------------------------------------------------------- inline float NormalizeAngle( float angle ) { if ( angle < 0.0 ) angle += 360.0; else if ( angle >= 360.0 ) angle -= 360.0; return angle; } //----------------------------------------------------------------------------- // ENUMERATIONS //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // STRUCTURES //----------------------------------------------------------------------------- //------------------------------------- // AI_Arc_t // // Purpose: Represents an arc. // //------------------------------------- struct AI_Arc_t { AI_Arc_t() : center( 0 ), span( 0 ) { } // Set by center and span void Set( float newCenter, float newSpan ); // Set by the right and left extremes (coordinates run counter clockwise) void SetByLimits( float yawRight, float yawLeft ); // Center of the arc (as "yaw") float center; // Span of the arc (in degrees) float span; }; //------------------------------------- // AI_MoveSuggestion_t // // Purpose: Suggests a possible move/avoidance, with a range of acceptable alternatives // // @Note (toml 06-20-02): this probably will eventually want to incorporate facing and // destination of the motivating goal. // //------------------------------------- enum AI_MoveSuggestionFlags_t { AIMS_FAVOR_LEFT = 0x01, AIMS_FAVOR_RIGHT = 0x02 }; //----------------- struct AI_MoveSuggestion_t { AI_MoveSuggestion_t(); AI_MoveSuggestion_t( AI_MoveSuggType_t newType, float newWeight, float newDir, float newSpan, CBaseEntity *pEntity = NULL ); AI_MoveSuggestion_t( AI_MoveSuggType_t newType, float newWeight, const AI_Arc_t &arc, CBaseEntity *pEntity = NULL ); void Set( AI_MoveSuggType_t newType, float newWeight, float newDir, float newSpan, CBaseEntity *pEntity = NULL ); void Set( AI_MoveSuggType_t newType, float newWeight, const AI_Arc_t &arc, CBaseEntity *pEntity = NULL ); //--------------------------------- // The kind of suggestion AI_MoveSuggType_t type; // The unadjusted weight of the suggestion [0..1], although [-1..1] within the solver float weight; // The desired direction to move/avoid AI_Arc_t arc; // The causing entity, if any EHANDLE hObstacleEntity; // Flags unsigned flags; }; //----------------- typedef CUtlVector CAI_MoveSuggestions; //------------------------------------- // AI_MoveSolution_t // // Purpose: The result of resolving suggestions // // @Note (toml 06-18-02): Currently, this is a very dopey little structure. // However, it will probably eventually incorporate much of the info // passed or calculated piecemeal between Move...() and Move...Execute() // functions. Once suggestions incorprate more information, the solution // may want to include a copy of the winning suggestion, so that the // caller need retain less state. If this is not the case, reduce it to just // a yaw. // //------------------------------------- struct AI_MoveSolution_t { AI_MoveSolution_t() : dir(0) { } // The direction to move float dir; }; //----------------------------------------------------------------------------- // class CAI_MoveSolver // // Purpose: Given a set of precalculated "regulations" (typically negative), // and a set of instantaneous suggestions (usually positive) //----------------------------------------------------------------------------- class CAI_MoveSolver { public: CAI_MoveSolver(); //--------------------------------- // Purpose: A regulation is a suggestion that is kept around as a rule until // cleared. They are generally negative suggestions. //--------------------------------- void AddRegulation( const AI_MoveSuggestion_t &suggestion ); void AddRegulations( const AI_MoveSuggestion_t *pSuggestion, int nSuggestions ); bool HaveRegulations() const; void ClearRegulations(); //--------------------------------- // Purpose: Solve the move, picking the best direction from a set of suggestions, // after applying the regulations //--------------------------------- bool Solve( const AI_MoveSuggestion_t *pSuggestions, int nSuggestions, AI_MoveSolution_t *pResult ); bool Solve( const AI_MoveSuggestion_t &suggestion, AI_MoveSolution_t *pResult ); //--------------------------------- bool HaveRegulationForObstacle( CBaseEntity *pEntity); //--------------------------------- // Visualization void VisualizeRegulations( const Vector& origin ); private: enum { REGS_RESERVE = 8, }; //--------------------------------- void NormalizeSuggestions( AI_MoveSuggestion_t *pBegin, AI_MoveSuggestion_t *pEnd ); //--------------------------------- CAI_MoveSuggestions m_Regulations; }; //----------------------------------------------------------------------------- // AI_Arc_t inline methods //----------------------------------------------------------------------------- inline void AI_Arc_t::Set( float newCenter, float newSpan ) { center = NormalizeAngle( newCenter ); span = NormalizeAngle( newSpan ); } //------------------------------------- inline void AI_Arc_t::SetByLimits( float yawRight, float yawLeft ) { // Yaw runs counter-clockwise span = yawLeft - yawRight; if ( span < 0 ) span += 360; center = yawRight + span * 0.5; if ( center >= 360 ) center -= 360; } //----------------------------------------------------------------------------- // AI_MoveSuggestion_t inline methods //----------------------------------------------------------------------------- inline void AI_MoveSuggestion_t::Set( AI_MoveSuggType_t newType, float newWeight, float newDir, float newSpan, CBaseEntity *pEntity ) { type = newType; weight = newWeight; hObstacleEntity = pEntity; flags = 0; arc.Set( newDir, newSpan ); } //------------------------------------- inline AI_MoveSuggestion_t::AI_MoveSuggestion_t() : type( AIMS_INVALID ), weight( 0 ), flags( 0 ) { } //------------------------------------- inline AI_MoveSuggestion_t::AI_MoveSuggestion_t( AI_MoveSuggType_t newType, float newWeight, float newDir, float newSpan, CBaseEntity *pEntity ) { Set( newType, newWeight, newDir, newSpan, pEntity ); } //------------------------------------- inline AI_MoveSuggestion_t::AI_MoveSuggestion_t( AI_MoveSuggType_t newType, float newWeight, const AI_Arc_t &arc, CBaseEntity *pEntity ) { Set( newType, newWeight, arc.center, arc.span, pEntity ); } //------------------------------------- inline void AI_MoveSuggestion_t::Set( AI_MoveSuggType_t newType, float newWeight, const AI_Arc_t &arc, CBaseEntity *pEntity ) { Set( newType, newWeight, arc.center, arc.span, pEntity ); } //----------------------------------------------------------------------------- // CAI_MoveSolver inline methods //----------------------------------------------------------------------------- inline CAI_MoveSolver::CAI_MoveSolver() { m_Regulations.EnsureCapacity( REGS_RESERVE ); } //------------------------------------- inline void CAI_MoveSolver::AddRegulation( const AI_MoveSuggestion_t &suggestion ) { m_Regulations.AddToTail( suggestion ); } //------------------------------------- inline void CAI_MoveSolver::AddRegulations( const AI_MoveSuggestion_t *pSuggestions, int nSuggestions ) { for (int i = 0; i < nSuggestions; ++i) { m_Regulations.AddToTail( pSuggestions[i] ); } } //------------------------------------- inline bool CAI_MoveSolver::HaveRegulations() const { return (m_Regulations.Count() > 0); } //------------------------------------- inline void CAI_MoveSolver::ClearRegulations() { m_Regulations.RemoveAll(); } //------------------------------------- inline bool CAI_MoveSolver::Solve( const AI_MoveSuggestion_t &suggestion, AI_MoveSolution_t *pResult) { return Solve( &suggestion, 1, pResult); } //============================================================================= #endif // AI_MOVESOLVER_H