Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1020 lines
43 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. // nav_area.h
  9. // Navigation areas
  10. // Author: Michael S. Booth ([email protected]), January 2003
  11. #ifndef _NAV_AREA_H_
  12. #define _NAV_AREA_H_
  13. #include "nav_ladder.h"
  14. #include "tier1/memstack.h"
  15. // BOTPORT: Clean up relationship between team index and danger storage in nav areas
  16. enum { MAX_NAV_TEAMS = 2 };
  17. class CFuncElevator;
  18. class CNavVectorNoEditAllocator
  19. {
  20. public:
  21. CNavVectorNoEditAllocator();
  22. static void Reset();
  23. static void *Alloc( size_t nSize );
  24. static void *Realloc( void *pMem, size_t nSize );
  25. static void Free( void *pMem );
  26. static size_t GetSize( void *pMem );
  27. private:
  28. static CMemoryStack m_memory;
  29. static void *m_pCurrent;
  30. static int m_nBytesCurrent;
  31. };
  32. #if !defined(_GAMECONSOLE)
  33. typedef CUtlVectorUltraConservativeAllocator CNavVectorAllocator;
  34. #else
  35. typedef CNavVectorNoEditAllocator CNavVectorAllocator;
  36. #endif
  37. //-------------------------------------------------------------------------------------------------------------------
  38. /**
  39. * Functor interface for iteration
  40. */
  41. class IForEachNavArea
  42. {
  43. public:
  44. virtual bool Inspect( const CNavArea *area ) = 0; // Invoked once on each area of the iterated set. Return false to stop iterating.
  45. virtual void PostIteration( bool wasCompleteIteration ) { } // Invoked after the iteration has ended. 'wasCompleteIteration' will be true if the entire set was iterated (ie: Inspect() never returned false)
  46. };
  47. //-------------------------------------------------------------------------------------------------------------------
  48. /**
  49. * The NavConnect union is used to refer to connections to areas
  50. */
  51. struct NavConnect
  52. {
  53. #if !defined( __clang__ )
  54. NavConnect()
  55. {
  56. id = 0;
  57. length = -1;
  58. }
  59. #endif
  60. union
  61. {
  62. unsigned int id;
  63. CNavArea *area;
  64. };
  65. mutable float length;
  66. bool operator==( const NavConnect &other ) const
  67. {
  68. return (area == other.area) ? true : false;
  69. }
  70. };
  71. typedef CUtlVectorUltraConservative<NavConnect, CNavVectorAllocator> NavConnectVector;
  72. //-------------------------------------------------------------------------------------------------------------------
  73. /**
  74. * The NavLadderConnect union is used to refer to connections to ladders
  75. */
  76. union NavLadderConnect
  77. {
  78. unsigned int id;
  79. CNavLadder *ladder;
  80. bool operator==( const NavLadderConnect &other ) const
  81. {
  82. return (ladder == other.ladder) ? true : false;
  83. }
  84. };
  85. typedef CUtlVectorUltraConservative<NavLadderConnect, CNavVectorAllocator> NavLadderConnectVector;
  86. //--------------------------------------------------------------------------------------------------------------
  87. /**
  88. * A HidingSpot is a good place for a bot to crouch and wait for enemies
  89. */
  90. class HidingSpot
  91. {
  92. public:
  93. enum
  94. {
  95. IN_COVER = 0x01, // in a corner with good hard cover nearby
  96. GOOD_SNIPER_SPOT = 0x02, // had at least one decent sniping corridor
  97. IDEAL_SNIPER_SPOT = 0x04, // can see either very far, or a large area, or both
  98. EXPOSED = 0x08 // spot in the open, usually on a ledge or cliff
  99. };
  100. bool HasGoodCover( void ) const { return (m_flags & IN_COVER) ? true : false; } // return true if hiding spot in in cover
  101. bool IsGoodSniperSpot( void ) const { return (m_flags & GOOD_SNIPER_SPOT) ? true : false; }
  102. bool IsIdealSniperSpot( void ) const { return (m_flags & IDEAL_SNIPER_SPOT) ? true : false; }
  103. bool IsExposed( void ) const { return (m_flags & EXPOSED) ? true : false; }
  104. int GetFlags( void ) const { return m_flags; }
  105. void Save( CUtlBuffer &fileBuffer, unsigned int version ) const;
  106. void Load( CUtlBuffer &fileBuffer, unsigned int version );
  107. NavErrorType PostLoad( void );
  108. const Vector &GetPosition( void ) const { return m_pos; } // get the position of the hiding spot
  109. unsigned int GetID( void ) const { return m_id; }
  110. const CNavArea *GetArea( void ) const { return m_area; } // return nav area this hiding spot is within
  111. void SetArea( CNavArea *pArea ) { m_area = pArea; }
  112. void Mark( void ) { m_marker = m_masterMarker; }
  113. bool IsMarked( void ) const { return (m_marker == m_masterMarker) ? true : false; }
  114. static void ChangeMasterMarker( void ) { ++m_masterMarker; }
  115. void SetSaved( bool bSaved ) { m_bIsSaved = bSaved; }
  116. bool IsSaved( void ) const { return m_bIsSaved; }
  117. public:
  118. void SetFlags( int flags ) { m_flags |= flags; } // FOR INTERNAL USE ONLY
  119. void SetPosition( const Vector &pos ) { m_pos = pos; } // FOR INTERNAL USE ONLY
  120. private:
  121. friend class CNavMesh;
  122. friend void ClassifySniperSpot( HidingSpot *spot );
  123. HidingSpot( void ); // must use factory to create
  124. Vector m_pos; // world coordinates of the spot
  125. unsigned int m_id; // this spot's unique ID
  126. unsigned int m_marker; // this spot's unique marker
  127. CNavArea *m_area; // the nav area containing this hiding spot
  128. unsigned char m_flags; // bit flags
  129. bool m_bIsSaved; // this hiding spot saves to the nav file (spots specified by entities do not save)
  130. static unsigned int m_nextID; // used when allocating spot ID's
  131. static unsigned int m_masterMarker; // used to mark spots
  132. };
  133. typedef CUtlVectorUltraConservative< HidingSpot * > HidingSpotVector;
  134. extern HidingSpotVector TheHidingSpots;
  135. extern HidingSpot *GetHidingSpotByID( unsigned int id );
  136. //--------------------------------------------------------------------------------------------------------------
  137. /**
  138. * Stores a pointer to an interesting "spot", and a parametric distance along a path
  139. */
  140. struct SpotOrder
  141. {
  142. float t; // parametric distance along ray where this spot first has LOS to our path
  143. union
  144. {
  145. HidingSpot *spot; // the spot to look at
  146. unsigned int id; // spot ID for save/load
  147. };
  148. };
  149. typedef CUtlVector< SpotOrder > SpotOrderVector;
  150. /**
  151. * This struct stores possible path segments thru a CNavArea, and the dangerous spots
  152. * to look at as we traverse that path segment.
  153. */
  154. struct SpotEncounter
  155. {
  156. NavConnect from;
  157. NavDirType fromDir;
  158. NavConnect to;
  159. NavDirType toDir;
  160. Ray path; // the path segment
  161. SpotOrderVector spots; // list of spots to look at, in order of occurrence
  162. };
  163. typedef CUtlVectorUltraConservative< SpotEncounter * > SpotEncounterVector;
  164. //-------------------------------------------------------------------------------------------------------------------
  165. /**
  166. * A CNavArea is a rectangular region defining a walkable area in the environment
  167. */
  168. class CNavAreaCriticalData
  169. {
  170. protected:
  171. // --- Begin critical data, which is heavily hit during pathing operations and carefully arranged for cache performance [7/24/2008 tom] ---
  172. /* 0 */ Vector m_nwCorner; // north-west corner position (2D mins)
  173. /* 12 */ Vector m_seCorner; // south-east corner position (2D maxs)
  174. /* 24 */ float m_invDxCorners;
  175. /* 28 */ float m_invDyCorners;
  176. /* 32 */ float m_neZ; // height of the implicit corner defined by (m_seCorner.x, m_nwCorner.y, m_neZ)
  177. /* 36 */ float m_swZ; // height of the implicit corner defined by (m_nwCorner.x, m_seCorner.y, m_neZ)
  178. /* 40 */ float m_danger[ MAX_NAV_TEAMS ]; // danger of this area, allowing bots to avoid areas where they died in the past - zero is no danger
  179. /* 48 */ float m_dangerTimestamp; // time when danger value was set - used for decaying
  180. /* 52 */ int m_damagingTickCount;
  181. /* 56 */ unsigned char m_playerCount[ MAX_NAV_TEAMS ]; // the number of players currently in this area
  182. /* 58 */ unsigned char m_isBlocked[ MAX_NAV_TEAMS ]; // if true, some part of the world is preventing movement through this nav area
  183. /* 60 */ float m_totalCost; // the distance so far plus an estimate of the distance left
  184. /* 64 */ float m_costSoFar; // distance travelled so far
  185. /* 68 */ CNavArea *m_nextOpen, *m_prevOpen; // only valid if m_openMarker == m_masterMarker
  186. /* 76 */ uint32 m_openMarker; // if this equals the current marker value, we are on the open list
  187. /* 80 */ uint32 m_marker; // used to flag the area as visited
  188. /* 84 */ int m_attributeFlags; // set of attribute bit flags (see NavAttributeType)
  189. //- connections to adjacent areas -------------------------------------------------------------------
  190. /* 88 */ NavConnectVector m_connect[ NUM_DIRECTIONS ]; // a list of adjacent areas for each direction
  191. /* 104*/ NavLadderConnectVector m_ladder[ CNavLadder::NUM_LADDER_DIRECTIONS ]; // list of ladders leading up and down from this area
  192. /* 112 */ uint32 m_nearNavSearchMarker; // used in GetNearestNavArea()
  193. /* 116 */ uint16 m_isUnderwater : 15; // true if the center of the area is underwater
  194. /* 116.1 */ uint16 m_isInheritedFrom : 1; // latch used during visibility inheritance computation
  195. /* 118 */ uint16 /* NavTraverseType */ m_parentHow; // how we get from parent to us
  196. /* 120*/ CNavArea *m_parent; // the area just prior to this on in the search path
  197. /* 124*/ float m_pathLengthSoFar; // length of path so far, needed for limiting pathfind max path length
  198. /* *************** 360 cache line *************** */
  199. // --- End critical data ---
  200. };
  201. class CNavArea : public CNavAreaCriticalData
  202. {
  203. public:
  204. DECLARE_CLASS_NOBASE( CNavArea )
  205. CNavArea( void );
  206. virtual ~CNavArea();
  207. virtual void OnServerActivate( void ); // (EXTEND) invoked when map is initially loaded
  208. virtual void OnRoundRestart( void ); // (EXTEND) invoked for each area when the round restarts
  209. virtual void OnRoundRestartPreEntity( void ) { } // invoked for each area when the round restarts, but before entities are deleted and recreated
  210. virtual void OnEnter( CBaseCombatCharacter *who, CNavArea *areaJustLeft ) { } // invoked when player enters this area
  211. virtual void OnExit( CBaseCombatCharacter *who, CNavArea *areaJustEntered ) { } // invoked when player exits this area
  212. virtual void OnDestroyNotify( CNavArea *dead ); // invoked when given area is going away
  213. virtual void OnDestroyNotify( CNavLadder *dead ); // invoked when given ladder is going away
  214. virtual void OnEditCreateNotify( CNavArea *newArea ) { } // invoked when given area has just been added to the mesh in edit mode
  215. virtual void OnEditDestroyNotify( CNavArea *deadArea ) { } // invoked when given area has just been deleted from the mesh in edit mode
  216. virtual void OnEditDestroyNotify( CNavLadder *deadLadder ) { } // invoked when given ladder has just been deleted from the mesh in edit mode
  217. virtual void Save( CUtlBuffer &fileBuffer, unsigned int version ) const; // (EXTEND)
  218. virtual NavErrorType Load( CUtlBuffer &fileBuffer, unsigned int version, unsigned int subVersion ); // (EXTEND)
  219. virtual NavErrorType PostLoad( void ); // (EXTEND) invoked after all areas have been loaded - for pointer binding, etc
  220. virtual void SaveToSelectedSet( KeyValues *areaKey ) const; // (EXTEND) saves attributes for the area to a KeyValues
  221. virtual void RestoreFromSelectedSet( KeyValues *areaKey ); // (EXTEND) restores attributes from a KeyValues
  222. // for interactively building or generating nav areas
  223. void Build( CNavNode *nwNode, CNavNode *neNode, CNavNode *seNode, CNavNode *swNode );
  224. void Build( const Vector &corner, const Vector &otherCorner );
  225. void Build( const Vector &nwCorner, const Vector &neCorner, const Vector &seCorner, const Vector &swCorner );
  226. void ConnectTo( CNavArea *area, NavDirType dir ); // connect this area to given area in given direction
  227. void Disconnect( CNavArea *area ); // disconnect this area from given area
  228. void ConnectTo( CNavLadder *ladder ); // connect this area to given ladder
  229. void Disconnect( CNavLadder *ladder ); // disconnect this area from given ladder
  230. unsigned int GetID( void ) const { return m_id; } // return this area's unique ID
  231. static void CompressIDs( void ); // re-orders area ID's so they are continuous
  232. unsigned int GetDebugID( void ) const { return 0; } // not used, need the space for cache optimization
  233. void SetAttributes( int bits ) { m_attributeFlags = bits; }
  234. int GetAttributes( void ) const { return m_attributeFlags; }
  235. bool HasAttributes( int bits ) const { return ( m_attributeFlags & bits ) ? true : false; }
  236. void RemoveAttributes( int bits ) { m_attributeFlags &= ( ~bits ); }
  237. void SetPlace( Place place ) { m_place = place; } // set place descriptor
  238. Place GetPlace( void ) const { return m_place; } // get place descriptor
  239. void MarkAsBlocked( int teamID, CBaseEntity *blocker, bool bGenerateEvent = true ); // An entity can force a nav area to be blocked
  240. void MarkAsUnblocked( int teamID, bool bGenerateEvent = true ); // An entity can force a nav area to be blocked
  241. virtual void UpdateBlocked( bool force = false, int teamID = TEAM_ANY ); // Updates the (un)blocked status of the nav area (throttled)
  242. virtual bool IsBlocked( int teamID, bool ignoreNavBlockers = false ) const;
  243. void CheckFloor( CBaseEntity *ignore ); // Checks if there is a floor under the nav area, in case a breakable floor is gone
  244. void MarkObstacleToAvoid( float obstructionHeight );
  245. void UpdateAvoidanceObstacles( void );
  246. bool HasAvoidanceObstacle( float maxObstructionHeight = StepHeight ) const; // is there a large, immobile object obstructing this area
  247. float GetAvoidanceObstacleHeight( void ) const; // returns the maximum height of the obstruction above the ground
  248. void CheckWaterLevel( void );
  249. bool IsUnderwater( void ) const { return !!m_isUnderwater; }
  250. bool IsOverlapping( const Vector &pos, float tolerance = 0.0f ) const; // return true if 'pos' is within 2D extents of area.
  251. bool IsOverlapping( const CNavArea *area ) const; // return true if 'area' overlaps our 2D extents
  252. bool IsOverlapping( const Extent &extent ) const; // return true if 'extent' overlaps our 2D extents
  253. bool IsOverlappingX( const CNavArea *area ) const; // return true if 'area' overlaps our X extent
  254. bool IsOverlappingY( const CNavArea *area ) const; // return true if 'area' overlaps our Y extent
  255. inline float GetZ( const Vector * RESTRICT pPos ) const RESTRICT; // return Z of area at (x,y) of 'pos'
  256. inline float GetZ( const Vector &pos ) const RESTRICT; // return Z of area at (x,y) of 'pos'
  257. float GetZ( float x, float y ) const RESTRICT; // return Z of area at (x,y) of 'pos'
  258. bool Contains( const Vector &pos ) const; // return true if given point is on or above this area, but no others
  259. bool Contains( const CNavArea *area ) const;
  260. bool IsCoplanar( const CNavArea *area ) const; // return true if this area and given area are approximately co-planar
  261. void GetClosestPointOnArea( const Vector * RESTRICT pPos, Vector *close ) const RESTRICT; // return closest point to 'pos' on this area - returned point in 'close'
  262. void GetClosestPointOnArea( const Vector &pos, Vector *close ) const { return GetClosestPointOnArea( &pos, close ); }
  263. float GetDistanceSquaredToPoint( const Vector &pos ) const; // return shortest distance between point and this area
  264. bool IsDegenerate( void ) const; // return true if this area is badly formed
  265. bool IsRoughlySquare( void ) const; // return true if this area is approximately square
  266. bool IsFlat( void ) const; // return true if this area is approximately flat
  267. bool HasNodes( void ) const;
  268. void GetNodes( NavDirType dir, CUtlVector< CNavNode * > *nodes ) const; // build a vector of nodes along the given direction
  269. CNavNode *FindClosestNode( const Vector &pos, NavDirType dir ) const; // returns the closest node along the given edge to the given point
  270. bool IsContiguous( const CNavArea *other ) const; // return true if the given area and 'other' share a colinear edge (ie: no drop-down or step/jump/climb)
  271. float ComputeAdjacentConnectionHeightChange( const CNavArea *destinationArea ) const; // return height change between edges of adjacent nav areas (not actual underlying ground)
  272. bool IsEdge( NavDirType dir ) const; // return true if there are no bi-directional links on the given side
  273. bool IsDamaging( void ) const; // Return true if continuous damage (ie: fire) is in this area
  274. void MarkAsDamaging( float duration ); // Mark this area is damaging for the next 'duration' seconds
  275. bool IsVisible( const Vector &eye, Vector *visSpot = NULL ) const; // return true if area is visible from the given eyepoint, return visible spot
  276. int GetAdjacentCount( NavDirType dir ) const { return m_connect[ dir ].Count(); } // return number of connected areas in given direction
  277. CNavArea *GetAdjacentArea( NavDirType dir, int i ) const; // return the i'th adjacent area in the given direction
  278. CNavArea *GetRandomAdjacentArea( NavDirType dir ) const;
  279. const NavConnectVector *GetAdjacentAreas( NavDirType dir ) const { return &m_connect[dir]; }
  280. bool IsConnected( const CNavArea *area, NavDirType dir ) const; // return true if given area is connected in given direction
  281. bool IsConnected( const CNavLadder *ladder, CNavLadder::LadderDirectionType dir ) const; // return true if given ladder is connected in given direction
  282. float ComputeGroundHeightChange( const CNavArea *area ); // compute change in actual ground height from this area to given area
  283. const NavConnectVector *GetIncomingConnections( NavDirType dir ) const { return &m_incomingConnect[dir]; } // get areas connected TO this area by a ONE-WAY link (ie: we have no connection back to them)
  284. void AddIncomingConnection( CNavArea *source, NavDirType incomingEdgeDir );
  285. const NavLadderConnectVector *GetLadders( CNavLadder::LadderDirectionType dir ) const { return &m_ladder[dir]; }
  286. void ComputePortal( const CNavArea *to, NavDirType dir, Vector *center, float *halfWidth ) const; // compute portal to adjacent area
  287. NavDirType ComputeLargestPortal( const CNavArea *to, Vector *center, float *halfWidth ) const; // compute largest portal to adjacent area, returning direction
  288. void ComputeClosestPointInPortal( const CNavArea *to, NavDirType dir, const Vector &fromPos, Vector *closePos ) const; // compute closest point within the "portal" between to adjacent areas
  289. NavDirType ComputeDirection( Vector *point ) const; // return direction from this area to the given point
  290. //- for hunting algorithm ---------------------------------------------------------------------------
  291. void SetClearedTimestamp( int teamID ); // set this area's "clear" timestamp to now
  292. float GetClearedTimestamp( int teamID ) const; // get time this area was marked "clear"
  293. //- hiding spots ------------------------------------------------------------------------------------
  294. const HidingSpotVector *GetHidingSpots( void ) const { return &m_hidingSpots; }
  295. void AddHidingSpot( HidingSpot * pSpot );
  296. void RemoveHidingSpot( HidingSpot * pSpot );
  297. unsigned char GetSavedHidingSpotCount( void ) const;
  298. SpotEncounter *GetSpotEncounter( const CNavArea *from, const CNavArea *to ); // given the areas we are moving between, return the spots we will encounter
  299. int GetSpotEncounterCount( void ) const { return m_spotEncounters.Count(); }
  300. //- "danger" ----------------------------------------------------------------------------------------
  301. void IncreaseDanger( int teamID, float amount ); // increase the danger of this area for the given team
  302. float GetDanger( int teamID ); // return the danger of this area (decays over time)
  303. virtual float GetDangerDecayRate( void ) const; // return danger decay rate per second
  304. //- extents -----------------------------------------------------------------------------------------
  305. float GetSizeX( void ) const { return m_seCorner.x - m_nwCorner.x; }
  306. float GetSizeY( void ) const { return m_seCorner.y - m_nwCorner.y; }
  307. void GetExtent( Extent *extent ) const; // return a computed extent (XY is in m_nwCorner and m_seCorner, Z is computed)
  308. Vector GetCenter( void ) const
  309. {
  310. Vector center;
  311. center.x = (m_nwCorner.x + m_seCorner.x)/2.0f;
  312. center.y = (m_nwCorner.y + m_seCorner.y)/2.0f;
  313. center.z = (m_nwCorner.z + m_seCorner.z)/2.0f;
  314. return center;
  315. }
  316. Vector GetRandomPoint( void ) const;
  317. Vector GetCorner( NavCornerType corner ) const;
  318. void SetCorner( NavCornerType corner, const Vector& newPosition );
  319. void ComputeNormal( Vector *normal, bool alternate = false ) const; // Computes the area's normal based on m_nwCorner. If 'alternate' is specified, m_seCorner is used instead.
  320. void RemoveOrthogonalConnections( NavDirType dir );
  321. //- occupy time ------------------------------------------------------------------------------------
  322. virtual float GetEarliestOccupyTime( int teamID ) const; // returns the minimum time for someone of the given team to reach this spot from their spawn
  323. //- player counting --------------------------------------------------------------------------------
  324. void IncrementPlayerCount( int teamID, int entIndex ); // add one player to this area's count
  325. void DecrementPlayerCount( int teamID, int entIndex ); // subtract one player from this area's count
  326. unsigned char GetPlayerCount( int teamID = 0 ) const; // return number of players of given team currently within this area (team of zero means any/all)
  327. //- lighting ----------------------------------------------------------------------------------------
  328. float GetLightIntensity( const Vector &pos ) const; // returns a 0..1 light intensity for the given point
  329. float GetLightIntensity( float x, float y ) const; // returns a 0..1 light intensity for the given point
  330. float GetLightIntensity( void ) const; // returns a 0..1 light intensity averaged over the whole area
  331. //- A* pathfinding algorithm ------------------------------------------------------------------------
  332. static void MakeNewMarker( void ) { ++m_masterMarker; if (m_masterMarker == 0) m_masterMarker = 1; }
  333. void Mark( void ) { m_marker = m_masterMarker; }
  334. BOOL IsMarked( void ) const { return (m_marker == m_masterMarker) ? true : false; }
  335. void SetParent( CNavArea *parent, NavTraverseType how = NUM_TRAVERSE_TYPES ) { m_parent = parent; m_parentHow = how; }
  336. CNavArea *GetParent( void ) const { return m_parent; }
  337. NavTraverseType GetParentHow( void ) const { return (NavTraverseType)m_parentHow; }
  338. bool IsOpen( void ) const; // true if on "open list"
  339. void AddToOpenList( void ); // add to open list in decreasing value order
  340. void AddToOpenListTail( void ); // add to tail of the open list
  341. void UpdateOnOpenList( void ); // a smaller value has been found, update this area on the open list
  342. void RemoveFromOpenList( void );
  343. static bool IsOpenListEmpty( void );
  344. static CNavArea *PopOpenList( void ); // remove and return the first element of the open list
  345. bool IsClosed( void ) const; // true if on "closed list"
  346. void AddToClosedList( void ); // add to the closed list
  347. void RemoveFromClosedList( void );
  348. static void ClearSearchLists( void ); // clears the open and closed lists for a new search
  349. void SetTotalCost( float value ) { Assert( value >= 0.0 && !IS_NAN(value) ); m_totalCost = value; }
  350. float GetTotalCost( void ) const { return m_totalCost; }
  351. void SetCostSoFar( float value ) { Assert( value >= 0.0 && !IS_NAN(value) ); m_costSoFar = value; }
  352. float GetCostSoFar( void ) const { return m_costSoFar; }
  353. void SetPathLengthSoFar( float value ) { Assert( value >= 0.0 && !IS_NAN(value) ); m_pathLengthSoFar = value; }
  354. float GetPathLengthSoFar( void ) const { return m_pathLengthSoFar; }
  355. //- editing -----------------------------------------------------------------------------------------
  356. virtual void Draw( void ) const; // draw area for debugging & editing
  357. virtual void DrawFilled( int r, int g, int b, int a, float deltaT = 0.1f, bool noDepthTest = true, float margin = 5.0f ) const; // draw area as a filled rect of the given color
  358. virtual void DrawSelectedSet( const Vector &shift ) const; // draw this area as part of a selected set
  359. void DrawDragSelectionSet( Color &dragSelectionSetColor ) const;
  360. void DrawConnectedAreas( void ) const;
  361. void DrawHidingSpots( void ) const;
  362. bool SplitEdit( bool splitAlongX, float splitEdge, CNavArea **outAlpha = NULL, CNavArea **outBeta = NULL ); // split this area into two areas at the given edge
  363. bool MergeEdit( CNavArea *adj ); // merge this area and given adjacent area
  364. bool SpliceEdit( CNavArea *other ); // create a new area between this area and given area
  365. void RaiseCorner( NavCornerType corner, int amount, bool raiseAdjacentCorners = true ); // raise/lower a corner (or all corners if corner == NUM_CORNERS)
  366. void PlaceOnGround( NavCornerType corner, float inset = 0.0f ); // places a corner (or all corners if corner == NUM_CORNERS) on the ground
  367. NavCornerType GetCornerUnderCursor( void ) const;
  368. bool GetCornerHotspot( NavCornerType corner, Vector hotspot[NUM_CORNERS] ) const; // returns true if the corner is under the cursor
  369. void Shift( const Vector &shift ); // shift the nav area
  370. //- ladders -----------------------------------------------------------------------------------------
  371. void AddLadderUp( CNavLadder *ladder );
  372. void AddLadderDown( CNavLadder *ladder );
  373. //- generation and analysis -------------------------------------------------------------------------
  374. virtual void ComputeHidingSpots( void ); // analyze local area neighborhood to find "hiding spots" in this area - for map learning
  375. virtual void ComputeSniperSpots( void ); // analyze local area neighborhood to find "sniper spots" in this area - for map learning
  376. virtual void ComputeSpotEncounters( void ); // compute spot encounter data - for map learning
  377. virtual void ComputeEarliestOccupyTimes( void );
  378. virtual void CustomAnalysis( bool isIncremental = false ) { } // for game-specific analysis
  379. virtual bool ComputeLighting( void ); // compute 0..1 light intensity at corners and center (requires client via listenserver)
  380. bool TestStairs( void ); // Test an area for being on stairs
  381. virtual bool IsAbleToMergeWith( CNavArea *other ) const;
  382. virtual void InheritAttributes( CNavArea *first, CNavArea *second = NULL );
  383. //- visibility -------------------------------------------------------------------------------------
  384. enum VisibilityType
  385. {
  386. NOT_VISIBLE = 0x00,
  387. POTENTIALLY_VISIBLE = 0x01,
  388. COMPLETELY_VISIBLE = 0x02,
  389. };
  390. VisibilityType ComputeVisibility( const CNavArea *area, bool isPVSValid, bool bCheckPVS = true, bool *pOutsidePVS = NULL ) const; // do actual line-of-sight traces to determine if any part of given area is visible from this area
  391. void SetupPVS( void ) const;
  392. bool IsInPVS( void ) const; // return true if this area is within the current PVS
  393. struct AreaBindInfo // for pointer loading and binding
  394. {
  395. union
  396. {
  397. CNavArea *area;
  398. unsigned int id;
  399. };
  400. unsigned char attributes; // VisibilityType
  401. bool operator==( const AreaBindInfo &other ) const
  402. {
  403. return ( area == other.area );
  404. }
  405. };
  406. virtual bool IsEntirelyVisible( const Vector &eye, CBaseEntity *ignore = NULL ) const; // return true if entire area is visible from given eyepoint (CPU intensive)
  407. virtual bool IsPartiallyVisible( const Vector &eye, CBaseEntity *ignore = NULL ) const; // return true if any portion of the area is visible from given eyepoint (CPU intensive)
  408. virtual bool IsPotentiallyVisible( const CNavArea *area ) const; // return true if given area is potentially visible from somewhere in this area (very fast)
  409. virtual bool IsPotentiallyVisibleToTeam( int team ) const; // return true if any portion of this area is visible to anyone on the given team (very fast)
  410. virtual bool IsCompletelyVisible( const CNavArea *area ) const; // return true if given area is completely visible from somewhere in this area (very fast)
  411. virtual bool IsCompletelyVisibleToTeam( int team ) const; // return true if given area is completely visible from somewhere in this area by someone on the team (very fast)
  412. //-------------------------------------------------------------------------------------
  413. /**
  414. * Apply the functor to all navigation areas that are potentially
  415. * visible from this area.
  416. */
  417. template < typename Functor >
  418. bool ForAllPotentiallyVisibleAreas( Functor &func )
  419. {
  420. int i;
  421. ++s_nCurrVisTestCounter;
  422. for ( i=0; i<m_potentiallyVisibleAreas.Count(); ++i )
  423. {
  424. CNavArea *area = m_potentiallyVisibleAreas[i].area;
  425. if ( !area )
  426. continue;
  427. // If this assertion triggers, an area is in here twice!
  428. Assert( area->m_nVisTestCounter != s_nCurrVisTestCounter );
  429. area->m_nVisTestCounter = s_nCurrVisTestCounter;
  430. if ( m_potentiallyVisibleAreas[i].attributes == NOT_VISIBLE )
  431. continue;
  432. if ( func( area ) == false )
  433. return false;
  434. }
  435. // for each inherited area
  436. if ( !m_inheritVisibilityFrom.area )
  437. return true;
  438. CAreaBindInfoArray &inherited = m_inheritVisibilityFrom.area->m_potentiallyVisibleAreas;
  439. for ( i=0; i<inherited.Count(); ++i )
  440. {
  441. if ( !inherited[i].area )
  442. continue;
  443. // We may have visited this from m_potentiallyVisibleAreas
  444. if ( inherited[i].area->m_nVisTestCounter == s_nCurrVisTestCounter )
  445. continue;
  446. // Theoretically, this shouldn't matter. But, just in case!
  447. inherited[i].area->m_nVisTestCounter = s_nCurrVisTestCounter;
  448. if ( inherited[i].attributes == NOT_VISIBLE )
  449. continue;
  450. if ( func( inherited[i].area ) == false )
  451. return false;
  452. }
  453. return true;
  454. }
  455. //-------------------------------------------------------------------------------------
  456. /**
  457. * Apply the functor to all navigation areas that are
  458. * completely visible from somewhere in this area.
  459. */
  460. template < typename Functor >
  461. bool ForAllCompletelyVisibleAreas( Functor &func )
  462. {
  463. int i;
  464. ++s_nCurrVisTestCounter;
  465. for ( i=0; i<m_potentiallyVisibleAreas.Count(); ++i )
  466. {
  467. CNavArea *area = m_potentiallyVisibleAreas[i].area;
  468. if ( !area )
  469. continue;
  470. // If this assertion triggers, an area is in here twice!
  471. Assert( area->m_nVisTestCounter != s_nCurrVisTestCounter );
  472. area->m_nVisTestCounter = s_nCurrVisTestCounter;
  473. if ( ( m_potentiallyVisibleAreas[i].attributes & COMPLETELY_VISIBLE ) == 0 )
  474. continue;
  475. if ( func( area ) == false )
  476. return false;
  477. }
  478. if ( !m_inheritVisibilityFrom.area )
  479. return true;
  480. // for each inherited area
  481. CAreaBindInfoArray &inherited = m_inheritVisibilityFrom.area->m_potentiallyVisibleAreas;
  482. for ( i=0; i<inherited.Count(); ++i )
  483. {
  484. if ( !inherited[i].area )
  485. continue;
  486. // We may have visited this from m_potentiallyVisibleAreas
  487. if ( inherited[i].area->m_nVisTestCounter == s_nCurrVisTestCounter )
  488. continue;
  489. // Theoretically, this shouldn't matter. But, just in case!
  490. inherited[i].area->m_nVisTestCounter = s_nCurrVisTestCounter;
  491. if ( ( inherited[i].attributes & COMPLETELY_VISIBLE ) == 0 )
  492. continue;
  493. if ( func( inherited[i].area ) == false )
  494. return false;
  495. }
  496. return true;
  497. }
  498. protected:
  499. void UnblockArea( void );
  500. private:
  501. friend class CNavMesh;
  502. friend class CNavLadder;
  503. friend class CCSNavArea; // allow CS load code to complete replace our default load behavior
  504. static bool m_isReset; // if true, don't bother cleaning up in destructor since everything is going away
  505. /*
  506. m_nwCorner
  507. nw ne
  508. +-----------+
  509. | +-->x |
  510. | | |
  511. | v |
  512. | y |
  513. | |
  514. +-----------+
  515. sw se
  516. m_seCorner
  517. */
  518. static unsigned int m_nextID; // used to allocate unique IDs
  519. unsigned int m_id; // unique area ID
  520. Place m_place; // place descriptor
  521. CountdownTimer m_blockedTimer; // Throttle checks on our blocked state while blocked
  522. void UpdateBlockedFromNavBlockers( void ); // checks if nav blockers are still blocking the area
  523. float m_avoidanceObstacleHeight; // if nonzero, a prop is obstructing movement through this nav area
  524. CountdownTimer m_avoidanceObstacleTimer; // Throttle checks on our obstructed state while obstructed
  525. //- for hunting -------------------------------------------------------------------------------------
  526. float m_clearedTimestamp[ MAX_NAV_TEAMS ]; // time this area was last "cleared" of enemies
  527. //- "danger" ----------------------------------------------------------------------------------------
  528. void DecayDanger( void );
  529. //- hiding spots ------------------------------------------------------------------------------------
  530. HidingSpotVector m_hidingSpots;
  531. bool IsHidingSpotCollision( const Vector &pos ) const; // returns true if an existing hiding spot is too close to given position
  532. //- encounter spots ---------------------------------------------------------------------------------
  533. SpotEncounterVector m_spotEncounters; // list of possible ways to move thru this area, and the spots to look at as we do
  534. void AddSpotEncounters( const CNavArea *from, NavDirType fromDir, const CNavArea *to, NavDirType toDir ); // add spot encounter data when moving from area to area
  535. float m_earliestOccupyTime[ MAX_NAV_TEAMS ]; // min time to reach this spot from spawn
  536. #ifdef DEBUG_AREA_PLAYERCOUNTS
  537. CUtlVector< int > m_playerEntIndices[ MAX_NAV_TEAMS ];
  538. #endif
  539. //- lighting ----------------------------------------------------------------------------------------
  540. float m_lightIntensity[ NUM_CORNERS ]; // 0..1 light intensity at corners
  541. //- A* pathfinding algorithm ------------------------------------------------------------------------
  542. static uint32 m_masterMarker;
  543. static CNavArea *m_openList;
  544. static CNavArea *m_openListTail;
  545. //- connections to adjacent areas -------------------------------------------------------------------
  546. NavConnectVector m_incomingConnect[ NUM_DIRECTIONS ]; // a list of adjacent areas for each direction that connect TO us, but we have no connection back to them
  547. //---------------------------------------------------------------------------------------------------
  548. CNavNode *m_node[ NUM_CORNERS ]; // nav nodes at each corner of the area
  549. void ResetNodes( void ); // nodes are going away as part of an incremental nav generation
  550. void Strip( void ); // remove "analyzed" data from nav area
  551. void FinishMerge( CNavArea *adjArea ); // recompute internal data once nodes have been adjusted during merge
  552. void MergeAdjacentConnections( CNavArea *adjArea ); // for merging with "adjArea" - pick up all of "adjArea"s connections
  553. void AssignNodes( CNavArea *area ); // assign internal nodes to the given area
  554. void FinishSplitEdit( CNavArea *newArea, NavDirType ignoreEdge ); // given the portion of the original area, update its internal data
  555. void CalcDebugID();
  556. CNavArea *m_prevHash, *m_nextHash; // for hash table in CNavMesh
  557. void ConnectElevators( void ); // find elevator connections between areas
  558. //- visibility --------------------------------------------------------------------------------------
  559. void ComputeVisibilityToMesh( void ); // compute visibility to surrounding mesh
  560. void ResetPotentiallyVisibleAreas();
  561. static void ComputeVisToArea( CNavArea *&pOtherArea );
  562. typedef CUtlVectorConservative<AreaBindInfo> CAreaBindInfoArray; // shaves 8 bytes off structure caused by need to support editing
  563. AreaBindInfo m_inheritVisibilityFrom; // if non-NULL, m_potentiallyVisibleAreas becomes a list of additions and deletions (NOT_VISIBLE) to the list of this area
  564. // This does not appear to be used in CS:GO, so take it out of the instances for now
  565. CAreaBindInfoArray m_potentiallyVisibleAreas; // list of areas potentially visible from inside this area (after PostLoad(), use area portion of union)
  566. const CAreaBindInfoArray &ComputeVisibilityDelta( const CNavArea *other ) const; // return a list of the delta between our visibility list and the given adjacent area
  567. uint32 m_nVisTestCounter;
  568. static uint32 s_nCurrVisTestCounter;
  569. };
  570. typedef CUtlVector< CNavArea * > NavAreaVector;
  571. extern NavAreaVector TheNavAreas;
  572. //--------------------------------------------------------------------------------------------------------------
  573. //--------------------------------------------------------------------------------------------------------------
  574. //
  575. // Inlines
  576. //
  577. //--------------------------------------------------------------------------------------------------------------
  578. inline float CNavArea::GetDangerDecayRate( void ) const
  579. {
  580. // one kill == 1.0, which we will forget about in two minutes
  581. return 1.0f / 120.0f;
  582. }
  583. //--------------------------------------------------------------------------------------------------------------
  584. inline bool CNavArea::IsDegenerate( void ) const
  585. {
  586. return (m_nwCorner.x >= m_seCorner.x || m_nwCorner.y >= m_seCorner.y);
  587. }
  588. //--------------------------------------------------------------------------------------------------------------
  589. inline CNavArea *CNavArea::GetAdjacentArea( NavDirType dir, int i ) const
  590. {
  591. for( int iter = 0; iter < m_connect[dir].Count(); ++iter )
  592. {
  593. if (i == 0)
  594. return m_connect[dir][iter].area;
  595. --i;
  596. }
  597. return NULL;
  598. }
  599. //--------------------------------------------------------------------------------------------------------------
  600. inline bool CNavArea::IsOpen( void ) const
  601. {
  602. return (m_openMarker == m_masterMarker) ? true : false;
  603. }
  604. //--------------------------------------------------------------------------------------------------------------
  605. inline bool CNavArea::IsOpenListEmpty( void )
  606. {
  607. Assert( (m_openList && m_openList->m_prevOpen == NULL) || m_openList == NULL );
  608. return (m_openList) ? false : true;
  609. }
  610. //--------------------------------------------------------------------------------------------------------------
  611. inline CNavArea *CNavArea::PopOpenList( void )
  612. {
  613. Assert( (m_openList && m_openList->m_prevOpen == NULL) || m_openList == NULL );
  614. if ( m_openList )
  615. {
  616. CNavArea *area = m_openList;
  617. // disconnect from list
  618. area->RemoveFromOpenList();
  619. area->m_prevOpen = NULL;
  620. area->m_nextOpen = NULL;
  621. Assert( (m_openList && m_openList->m_prevOpen == NULL) || m_openList == NULL );
  622. return area;
  623. }
  624. Assert( (m_openList && m_openList->m_prevOpen == NULL) || m_openList == NULL );
  625. return NULL;
  626. }
  627. //--------------------------------------------------------------------------------------------------------------
  628. inline bool CNavArea::IsClosed( void ) const
  629. {
  630. if (IsMarked() && !IsOpen())
  631. return true;
  632. return false;
  633. }
  634. //--------------------------------------------------------------------------------------------------------------
  635. inline void CNavArea::AddToClosedList( void )
  636. {
  637. Mark();
  638. }
  639. //--------------------------------------------------------------------------------------------------------------
  640. inline void CNavArea::RemoveFromClosedList( void )
  641. {
  642. // since "closed" is defined as visited (marked) and not on open list, do nothing
  643. }
  644. //--------------------------------------------------------------------------------------------------------------
  645. inline void CNavArea::SetClearedTimestamp( int teamID )
  646. {
  647. m_clearedTimestamp[ teamID % MAX_NAV_TEAMS ] = gpGlobals->curtime;
  648. }
  649. //--------------------------------------------------------------------------------------------------------------
  650. inline float CNavArea::GetClearedTimestamp( int teamID ) const
  651. {
  652. return m_clearedTimestamp[ teamID % MAX_NAV_TEAMS ];
  653. }
  654. //--------------------------------------------------------------------------------------------------------------
  655. inline float CNavArea::GetEarliestOccupyTime( int teamID ) const
  656. {
  657. return m_earliestOccupyTime[ teamID % MAX_NAV_TEAMS ];
  658. }
  659. //--------------------------------------------------------------------------------------------------------------
  660. inline bool CNavArea::IsDamaging( void ) const
  661. {
  662. return ( gpGlobals->tickcount <= m_damagingTickCount );
  663. }
  664. //--------------------------------------------------------------------------------------------------------------
  665. inline void CNavArea::MarkAsDamaging( float duration )
  666. {
  667. m_damagingTickCount = gpGlobals->tickcount + TIME_TO_TICKS( duration );
  668. }
  669. //--------------------------------------------------------------------------------------------------------------
  670. inline bool CNavArea::HasAvoidanceObstacle( float maxObstructionHeight ) const
  671. {
  672. return m_avoidanceObstacleHeight > maxObstructionHeight;
  673. }
  674. //--------------------------------------------------------------------------------------------------------------
  675. inline float CNavArea::GetAvoidanceObstacleHeight( void ) const
  676. {
  677. return m_avoidanceObstacleHeight;
  678. }
  679. //--------------------------------------------------------------------------------------------------------------
  680. inline bool CNavArea::IsVisible( const Vector &eye, Vector *visSpot ) const
  681. {
  682. Vector corner;
  683. trace_t result;
  684. CTraceFilterNoNPCsOrPlayer traceFilter( NULL, COLLISION_GROUP_NONE );
  685. const float offset = 0.75f * HumanHeight;
  686. // check center first
  687. UTIL_TraceLine( eye, GetCenter() + Vector( 0, 0, offset ), MASK_BLOCKLOS_AND_NPCS|CONTENTS_IGNORE_NODRAW_OPAQUE, &traceFilter, &result );
  688. if (result.fraction == 1.0f)
  689. {
  690. // we can see this area
  691. if (visSpot)
  692. {
  693. *visSpot = GetCenter();
  694. }
  695. return true;
  696. }
  697. for( int c=0; c<NUM_CORNERS; ++c )
  698. {
  699. corner = GetCorner( (NavCornerType)c );
  700. UTIL_TraceLine( eye, corner + Vector( 0, 0, offset ), MASK_BLOCKLOS_AND_NPCS|CONTENTS_IGNORE_NODRAW_OPAQUE, &traceFilter, &result );
  701. if (result.fraction == 1.0f)
  702. {
  703. // we can see this area
  704. if (visSpot)
  705. {
  706. *visSpot = corner;
  707. }
  708. return true;
  709. }
  710. }
  711. return false;
  712. }
  713. #ifndef DEBUG_AREA_PLAYERCOUNTS
  714. inline void CNavArea::IncrementPlayerCount( int teamID, int entIndex )
  715. {
  716. teamID = teamID % MAX_NAV_TEAMS;
  717. if (m_playerCount[ teamID ] == 255)
  718. {
  719. DevMsg( "CNavArea::IncrementPlayerCount: Overflow\n" );
  720. return;
  721. }
  722. ++m_playerCount[ teamID ];
  723. }
  724. inline void CNavArea::DecrementPlayerCount( int teamID, int entIndex )
  725. {
  726. teamID = teamID % MAX_NAV_TEAMS;
  727. if (m_playerCount[ teamID ] == 0)
  728. {
  729. DevWarning( "CNavArea::IncrementPlayerCount: Underflow\n" );
  730. return;
  731. }
  732. --m_playerCount[ teamID ];
  733. }
  734. #endif // !DEBUG_AREA_PLAYERCOUNTS
  735. inline unsigned char CNavArea::GetPlayerCount( int teamID ) const
  736. {
  737. if (teamID)
  738. {
  739. return m_playerCount[ teamID % MAX_NAV_TEAMS ];
  740. }
  741. // sum all players
  742. unsigned char total = 0;
  743. for( int i=0; i<MAX_NAV_TEAMS; ++i )
  744. {
  745. total += m_playerCount[i];
  746. }
  747. return total;
  748. }
  749. //--------------------------------------------------------------------------------------------------------------
  750. /**
  751. * Return Z of area at (x,y) of 'pos'
  752. * Trilinear interpolation of Z values at quad edges.
  753. * NOTE: pos->z is not used.
  754. */
  755. inline float CNavArea::GetZ( const Vector * RESTRICT pos ) const RESTRICT
  756. {
  757. return GetZ( pos->x, pos->y );
  758. }
  759. inline float CNavArea::GetZ( const Vector & pos ) const RESTRICT
  760. {
  761. return GetZ( pos.x, pos.y );
  762. }
  763. //--------------------------------------------------------------------------------------------------------------
  764. /**
  765. * Return the coordinates of the area's corner.
  766. */
  767. inline Vector CNavArea::GetCorner( NavCornerType corner ) const
  768. {
  769. // @TODO: Confirm compiler does the "right thing" in release builds, or change this function to to take a pointer [2/4/2009 tom]
  770. Vector pos;
  771. switch( corner )
  772. {
  773. default:
  774. Assert( false && "GetCorner: Invalid type" );
  775. case NORTH_WEST:
  776. return m_nwCorner;
  777. case NORTH_EAST:
  778. pos.x = m_seCorner.x;
  779. pos.y = m_nwCorner.y;
  780. pos.z = m_neZ;
  781. return pos;
  782. case SOUTH_WEST:
  783. pos.x = m_nwCorner.x;
  784. pos.y = m_seCorner.y;
  785. pos.z = m_swZ;
  786. return pos;
  787. case SOUTH_EAST:
  788. return m_seCorner;
  789. }
  790. }
  791. #endif // _NAV_AREA_H_