2015-08-02 20:45:57 +03:00
/*
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation ; either version 2 of the License , or ( at
* your option ) any later version .
*
* This program is distributed in the hope that it will be useful , but
* WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software Foundation ,
* Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*
* In addition , as a special exception , the author gives permission to
* link the code of this program with the Half - Life Game Engine ( " HL
* Engine " ) and Modified Game Libraries ( " MODs " ) developed by Valve,
* L . L . C ( " Valve " ) . You must obey the GNU General Public License in all
* respects for all of the code used other than the HL Engine and MODs
* from Valve . If you modify this file , you may extend this exception
* to your version of the file , but you are not obligated to do so . If
* you do not wish to do so , delete this exception statement from your
* version .
*
*/
# pragma once
# include <list>
class CNavArea ;
2016-02-04 03:18:26 +03:00
void DestroyHidingSpots ( ) ;
void StripNavigationAreas ( ) ;
2015-12-22 21:07:49 +03:00
bool SaveNavigationMap ( const char * filename ) ;
2016-02-04 03:18:26 +03:00
NavErrorType LoadNavigationMap ( ) ;
void DestroyNavigationMap ( ) ;
2015-08-02 20:45:57 +03:00
enum NavEditCmdType
{
EDIT_NONE ,
2017-10-12 17:50:56 +03:00
EDIT_DELETE , // delete current area
EDIT_SPLIT , // split current area
EDIT_MERGE , // merge adjacent areas
EDIT_JOIN , // define connection between areas
EDIT_BREAK , // break connection between areas
EDIT_MARK , // mark an area for further operations
EDIT_ATTRIB_CROUCH , // toggle crouch attribute on current area
EDIT_ATTRIB_JUMP , // toggle jump attribute on current area
2015-12-22 21:07:49 +03:00
EDIT_ATTRIB_PRECISE , // toggle precise attribute on current area
EDIT_ATTRIB_NO_JUMP , // toggle inhibiting discontinuity jumping in current area
2017-10-12 17:50:56 +03:00
EDIT_BEGIN_AREA , // begin creating a new nav area
EDIT_END_AREA , // end creation of the new nav area
EDIT_CONNECT , // connect marked area to selected area
EDIT_DISCONNECT , // disconnect marked area from selected area
EDIT_SPLICE , // create new area in between marked and selected areas
2015-12-22 21:07:49 +03:00
EDIT_TOGGLE_PLACE_MODE , // switch between normal and place editing
EDIT_TOGGLE_PLACE_PAINTING , // switch between "painting" places onto areas
EDIT_PLACE_FLOODFILL , // floodfill areas out from current area
2017-10-12 17:50:56 +03:00
EDIT_PLACE_PICK , // "pick up" the place at the current area
EDIT_MARK_UNNAMED , // mark an unnamed area for further operations
EDIT_WARP_TO_MARK , // warp a spectating local player to the selected mark
EDIT_SELECT_CORNER , // select a corner on the current area
EDIT_RAISE_CORNER , // raise a corner on the current area
EDIT_LOWER_CORNER , // lower a corner on the current area
2015-08-02 20:45:57 +03:00
} ;
enum RouteType
{
FASTEST_ROUTE ,
SAFEST_ROUTE ,
} ;
union NavConnect
{
unsigned int id ;
CNavArea * area ;
2016-02-04 03:18:26 +03:00
bool operator = = ( const NavConnect & other ) const { return ( area = = other . area ) ? true : false ; }
2015-08-02 20:45:57 +03:00
} ;
2017-10-19 20:12:02 +03:00
typedef std : : list < NavConnect > NavConnectList ;
2015-08-02 20:45:57 +03:00
enum LadderDirectionType
{
LADDER_UP = 0 ,
LADDER_DOWN ,
NUM_LADDER_DIRECTIONS
} ;
class CNavLadder
{
public :
2016-02-04 03:18:26 +03:00
CNavLadder ( )
2015-08-02 20:45:57 +03:00
{
2017-10-12 17:50:56 +03:00
m_topForwardArea = nullptr ;
m_topRightArea = nullptr ;
m_topLeftArea = nullptr ;
m_topBehindArea = nullptr ;
m_bottomArea = nullptr ;
m_entity = nullptr ;
2015-08-02 20:45:57 +03:00
}
Vector m_top ;
Vector m_bottom ;
float m_length ;
NavDirType m_dir ;
Vector2D m_dirVector ;
CBaseEntity * m_entity ;
CNavArea * m_topForwardArea ;
CNavArea * m_topLeftArea ;
CNavArea * m_topRightArea ;
CNavArea * m_topBehindArea ;
CNavArea * m_bottomArea ;
bool m_isDangling ;
2016-04-05 03:12:05 +03:00
void OnDestroyNotify ( CNavArea * dead )
2015-08-02 20:45:57 +03:00
{
if ( dead = = m_topForwardArea )
2017-10-12 17:50:56 +03:00
m_topForwardArea = nullptr ;
2015-08-02 20:45:57 +03:00
if ( dead = = m_topLeftArea )
2017-10-12 17:50:56 +03:00
m_topLeftArea = nullptr ;
2015-08-02 20:45:57 +03:00
if ( dead = = m_topRightArea )
2017-10-12 17:50:56 +03:00
m_topRightArea = nullptr ;
2015-08-02 20:45:57 +03:00
if ( dead = = m_topBehindArea )
2017-10-12 17:50:56 +03:00
m_topBehindArea = nullptr ;
2015-08-02 20:45:57 +03:00
if ( dead = = m_bottomArea )
2017-10-12 17:50:56 +03:00
m_bottomArea = nullptr ;
2015-08-02 20:45:57 +03:00
}
2016-02-04 03:18:26 +03:00
} ;
2015-08-02 20:45:57 +03:00
2017-10-19 20:12:02 +03:00
typedef std : : list < CNavLadder * > NavLadderList ;
2016-12-06 22:21:52 +03:00
extern NavLadderList TheNavLadderList ;
2015-08-02 20:45:57 +03:00
class HidingSpot
{
public :
2016-02-04 03:18:26 +03:00
HidingSpot ( ) ;
2015-08-02 20:45:57 +03:00
HidingSpot ( const Vector * pos , unsigned char flags ) ;
enum
{
IN_COVER = 0x01 ,
GOOD_SNIPER_SPOT = 0x02 ,
IDEAL_SNIPER_SPOT = 0x04
} ;
2016-02-04 03:18:26 +03:00
bool HasGoodCover ( ) const { return ( m_flags & IN_COVER ) ? true : false ; }
bool IsGoodSniperSpot ( ) const { return ( m_flags & GOOD_SNIPER_SPOT ) ? true : false ; }
bool IsIdealSniperSpot ( ) const { return ( m_flags & IDEAL_SNIPER_SPOT ) ? true : false ; }
void SetFlags ( unsigned char flags ) { m_flags | = flags ; }
unsigned char GetFlags ( ) const { return m_flags ; }
2015-08-02 20:45:57 +03:00
void Save ( int fd , unsigned int version ) const ;
void Load ( SteamFile * file , unsigned int version ) ;
2016-02-04 03:18:26 +03:00
const Vector * GetPosition ( ) const { return & m_pos ; }
unsigned int GetID ( ) const { return m_id ; }
2017-10-19 20:12:02 +03:00
void Mark ( ) { m_marker = m_masterMarker ; }
bool IsMarked ( ) const { return ( m_marker = = m_masterMarker ) ? true : false ; }
2016-02-04 03:18:26 +03:00
2017-10-19 20:12:02 +03:00
static void ChangeMasterMarker ( ) { m_masterMarker + + ; }
2015-08-02 20:45:57 +03:00
private :
2016-02-04 03:18:26 +03:00
friend void DestroyHidingSpots ( ) ;
2015-08-02 20:45:57 +03:00
Vector m_pos ;
unsigned int m_id ;
unsigned int m_marker ;
unsigned char m_flags ;
2017-10-19 20:12:02 +03:00
static unsigned int m_nextID ;
static unsigned int m_masterMarker ;
2016-02-04 03:18:26 +03:00
} ;
2015-08-02 20:45:57 +03:00
2017-10-19 20:12:02 +03:00
typedef std : : list < HidingSpot * > HidingSpotList ;
2016-12-06 22:21:52 +03:00
extern HidingSpotList TheHidingSpotList ;
2015-08-02 20:45:57 +03:00
struct SpotOrder
{
float t ;
union
{
HidingSpot * spot ;
unsigned int id ;
} ;
2016-02-04 03:18:26 +03:00
} ;
2015-08-02 20:45:57 +03:00
2017-10-19 20:12:02 +03:00
typedef std : : list < SpotOrder > SpotOrderList ;
2015-08-02 20:45:57 +03:00
struct SpotEncounter
{
NavConnect from ;
NavDirType fromDir ;
NavConnect to ;
NavDirType toDir ;
2015-12-22 21:07:49 +03:00
Ray path ; // the path segment
2017-10-12 17:50:56 +03:00
SpotOrderList spotList ; // list of spots to look at, in order of occurrence
2016-02-04 03:18:26 +03:00
} ;
2015-08-02 20:45:57 +03:00
2017-10-19 20:12:02 +03:00
typedef std : : list < SpotEncounter > SpotEncounterList ;
typedef std : : list < CNavArea * > NavAreaList ;
2015-08-02 20:45:57 +03:00
2015-12-22 21:07:49 +03:00
// A CNavArea is a rectangular region defining a walkable area in the map
2015-08-02 20:45:57 +03:00
class CNavArea
{
public :
CNavArea ( CNavNode * nwNode , CNavNode * neNode , CNavNode * seNode , CNavNode * swNode ) ;
2016-02-04 03:18:26 +03:00
CNavArea ( ) ;
2015-08-02 20:45:57 +03:00
CNavArea ( const Vector * corner , const Vector * otherCorner ) ;
CNavArea ( const Vector * nwCorner , const Vector * neCorner , const Vector * seCorner , const Vector * swCorner ) ;
~ CNavArea ( ) ;
2017-10-12 17:50:56 +03:00
void ConnectTo ( CNavArea * area , NavDirType dir ) ; // connect this area to given area in given direction
2015-12-22 21:07:49 +03:00
void Disconnect ( CNavArea * area ) ; // disconnect this area from given area
2015-08-02 20:45:57 +03:00
void Save ( FILE * fp ) const ;
void Save ( int fd , unsigned int version ) ;
void Load ( SteamFile * file , unsigned int version ) ;
2016-02-04 03:18:26 +03:00
NavErrorType PostLoad ( ) ;
2015-08-02 20:45:57 +03:00
2016-02-23 02:13:52 +03:00
unsigned int GetID ( ) const { return m_id ; }
void SetAttributes ( unsigned char bits ) { m_attributeFlags = bits ; }
unsigned char GetAttributes ( ) const { return m_attributeFlags ; }
void SetPlace ( Place place ) { m_place = place ; } // set place descriptor
2017-10-12 17:50:56 +03:00
Place GetPlace ( ) const { return m_place ; } // get place descriptor
2016-01-19 14:54:31 +03:00
bool IsOverlapping ( const Vector * pos ) const ; // return true if 'pos' is within 2D extents of area
bool IsOverlapping ( const CNavArea * area ) const ; // return true if 'area' overlaps our 2D extents
bool IsOverlappingX ( const CNavArea * area ) const ; // return true if 'area' overlaps our X extent
bool IsOverlappingY ( const CNavArea * area ) const ; // return true if 'area' overlaps our Y extent
2017-10-12 17:50:56 +03:00
int GetPlayerCount ( int teamID = 0 , CBasePlayer * ignore = nullptr ) const ; // return number of players with given teamID in this area (teamID == 0 means any/all)
float GetZ ( const Vector * pos ) const ; // return Z of area at (x,y) of 'pos'
float GetZ ( float x , float y ) const ; // return Z of area at (x,y) of 'pos'
bool Contains ( const Vector * pos ) const ; // return true if given point is on or above this area, but no others
2016-01-19 14:54:31 +03:00
bool IsCoplanar ( const CNavArea * area ) const ; // return true if this area and given area are approximately co-planar
2017-10-12 17:50:56 +03:00
void GetClosestPointOnArea ( const Vector * pos , Vector * close ) const ; // return closest point to 'pos' on this area - returned point in 'close'
float GetDistanceSquaredToPoint ( const Vector * pos ) const ; // return shortest distance between point and this area
bool IsDegenerate ( ) const ; // return true if this area is badly formed
bool IsEdge ( NavDirType dir ) const ; // return true if there are no bi-directional links on the given side
2016-02-23 02:13:52 +03:00
int GetAdjacentCount ( NavDirType dir ) const { return m_connect [ dir ] . size ( ) ; } // return number of connected areas in given direction
2017-10-12 17:50:56 +03:00
CNavArea * GetAdjacentArea ( NavDirType dir , int i ) const ; // return the i'th adjacent area in the given direction
2015-08-02 20:45:57 +03:00
CNavArea * GetRandomAdjacentArea ( NavDirType dir ) const ;
2016-01-19 14:54:31 +03:00
const NavConnectList * GetAdjacentList ( NavDirType dir ) const { return & m_connect [ dir ] ; }
2017-10-12 17:50:56 +03:00
bool IsConnected ( const CNavArea * area , NavDirType dir ) const ; // return true if given area is connected in given direction
2016-01-19 14:54:31 +03:00
float ComputeHeightChange ( const CNavArea * area ) ; // compute change in height from this area to given area
2015-08-02 20:45:57 +03:00
2016-01-19 14:54:31 +03:00
const NavLadderList * GetLadderList ( LadderDirectionType dir ) const { return & m_ladder [ dir ] ; }
2015-08-02 20:45:57 +03:00
2017-10-12 17:50:56 +03:00
void ComputePortal ( const CNavArea * to , NavDirType dir , Vector * center , float * halfWidth ) const ; // compute portal to adjacent area
2015-12-22 21:07:49 +03:00
void ComputeClosestPointInPortal ( const CNavArea * to , NavDirType dir , const Vector * fromPos , Vector * closePos ) const ; // compute closest point within the "portal" between to adjacent areas
NavDirType ComputeDirection ( Vector * point ) const ; // return direction from this area to the given point
2015-08-02 20:45:57 +03:00
2015-12-22 21:07:49 +03:00
// for hunting algorithm
2016-01-19 14:54:31 +03:00
void SetClearedTimestamp ( int teamID ) { m_clearedTimestamp [ teamID ] = gpGlobals - > time ; } // set this area's "clear" timestamp to now
2017-10-12 17:50:56 +03:00
float GetClearedTimestamp ( int teamID ) { return m_clearedTimestamp [ teamID ] ; } // get time this area was marked "clear"
2015-12-22 21:07:49 +03:00
// hiding spots
2016-02-04 03:18:26 +03:00
const HidingSpotList * GetHidingSpotList ( ) const { return & m_hidingSpotList ; }
2016-01-19 14:54:31 +03:00
2016-02-04 03:18:26 +03:00
void ComputeHidingSpots ( ) ; // analyze local area neighborhood to find "hiding spots" in this area - for map learning
void ComputeSniperSpots ( ) ; // analyze local area neighborhood to find "sniper spots" in this area - for map learning
2015-08-02 20:45:57 +03:00
2015-12-22 21:07:49 +03:00
SpotEncounter * GetSpotEncounter ( const CNavArea * from , const CNavArea * to ) ; // given the areas we are moving between, return the spots we will encounter
2016-02-04 03:18:26 +03:00
void ComputeSpotEncounters ( ) ; // compute spot encounter data - for map learning
2015-08-02 20:45:57 +03:00
2015-12-22 21:07:49 +03:00
// danger
2017-10-12 17:50:56 +03:00
void IncreaseDanger ( int teamID , float amount ) ; // increase the danger of this area for the given team
2015-12-22 21:07:49 +03:00
float GetDanger ( int teamID ) ; // return the danger of this area (decays over time)
2016-01-19 14:54:31 +03:00
2016-02-04 03:18:26 +03:00
float GetSizeX ( ) const { return m_extent . hi . x - m_extent . lo . x ; }
float GetSizeY ( ) const { return m_extent . hi . y - m_extent . lo . y ; }
2016-01-19 14:54:31 +03:00
2016-02-04 03:18:26 +03:00
const Extent * GetExtent ( ) const { return & m_extent ; }
const Vector * GetCenter ( ) const { return & m_center ; }
2015-08-02 20:45:57 +03:00
const Vector * GetCorner ( NavCornerType corner ) const ;
2015-12-22 21:07:49 +03:00
// approach areas
2015-08-02 20:45:57 +03:00
struct ApproachInfo
{
2015-12-22 21:07:49 +03:00
NavConnect here ; // the approach area
NavConnect prev ; // the area just before the approach area on the path
2015-08-02 20:45:57 +03:00
NavTraverseType prevToHereHow ;
2015-12-22 21:07:49 +03:00
NavConnect next ; // the area just after the approach area on the path
2015-08-02 20:45:57 +03:00
NavTraverseType hereToNextHow ;
} ;
2016-01-19 14:54:31 +03:00
const ApproachInfo * GetApproachInfo ( int i ) const { return & m_approach [ i ] ; }
2016-02-04 03:18:26 +03:00
int GetApproachInfoCount ( ) const { return m_approachCount ; }
void ComputeApproachAreas ( ) ; // determine the set of "approach areas" - for map learning
2015-12-22 21:07:49 +03:00
// A* pathfinding algorithm
2016-02-04 03:18:26 +03:00
static void MakeNewMarker ( )
2015-08-02 20:45:57 +03:00
{
2017-10-19 20:12:02 +03:00
if ( + + m_masterMarker = = 0 )
m_masterMarker = 1 ;
2015-08-02 20:45:57 +03:00
}
2017-10-19 20:12:02 +03:00
void Mark ( ) { m_marker = m_masterMarker ; }
BOOL IsMarked ( ) const { return ( m_marker = = m_masterMarker ) ? true : false ; }
2016-01-19 14:54:31 +03:00
void SetParent ( CNavArea * parent , NavTraverseType how = NUM_TRAVERSE_TYPES ) { m_parent = parent ; m_parentHow = how ; }
2016-02-04 03:18:26 +03:00
CNavArea * GetParent ( ) const { return m_parent ; }
NavTraverseType GetParentHow ( ) const { return m_parentHow ; }
2015-08-02 20:45:57 +03:00
2016-02-04 03:18:26 +03:00
bool IsOpen ( ) const ; // true if on "open list"
void AddToOpenList ( ) ; // add to open list in decreasing value order
void UpdateOnOpenList ( ) ; // a smaller value has been found, update this area on the open list
void RemoveFromOpenList ( ) ;
static bool IsOpenListEmpty ( ) ;
static CNavArea * PopOpenList ( ) ; // remove and return the first element of the open list
2015-08-02 20:45:57 +03:00
2017-10-12 17:50:56 +03:00
bool IsClosed ( ) const ; // true if on "closed list"
void AddToClosedList ( ) ; // add to the closed list
2016-02-04 03:18:26 +03:00
void RemoveFromClosedList ( ) ;
2015-08-02 20:45:57 +03:00
2016-02-04 03:18:26 +03:00
static void ClearSearchLists ( ) ; // clears the open and closed lists for a new search
2015-08-02 20:45:57 +03:00
2016-02-23 02:13:52 +03:00
void SetTotalCost ( float value ) { m_totalCost = value ; }
float GetTotalCost ( ) const { return m_totalCost ; }
2016-02-04 03:18:26 +03:00
2016-02-23 02:13:52 +03:00
void SetCostSoFar ( float value ) { m_costSoFar = value ; }
float GetCostSoFar ( ) const { return m_costSoFar ; }
2015-08-02 20:45:57 +03:00
2015-12-22 21:07:49 +03:00
// editing
void Draw ( byte red , byte green , byte blue , int duration = 50 ) ; // draw area for debugging & editing
2016-02-04 03:18:26 +03:00
void DrawConnectedAreas ( ) ;
2015-08-02 20:45:57 +03:00
void DrawMarkedCorner ( NavCornerType corner , byte red , byte green , byte blue , int duration = 50 ) ;
2017-10-12 17:50:56 +03:00
bool SplitEdit ( bool splitAlongX , float splitEdge , CNavArea * * outAlpha = nullptr , CNavArea * * outBeta = nullptr ) ; // split this area into two areas at the given edge
2016-02-04 03:18:26 +03:00
bool MergeEdit ( CNavArea * adj ) ; // merge this area and given adjacent area
2015-12-22 21:07:49 +03:00
bool SpliceEdit ( CNavArea * other ) ; // create a new area between this area and given area
2017-10-12 17:50:56 +03:00
void RaiseCorner ( NavCornerType corner , int amount ) ; // raise/lower a corner (or all corners if corner == NUM_CORNERS)
2015-12-22 21:07:49 +03:00
// ladders
2016-01-19 14:54:31 +03:00
void AddLadderUp ( CNavLadder * ladder ) { m_ladder [ LADDER_UP ] . push_back ( ladder ) ; }
void AddLadderDown ( CNavLadder * ladder ) { m_ladder [ LADDER_DOWN ] . push_back ( ladder ) ; }
2015-08-02 20:45:57 +03:00
private :
2016-02-04 03:18:26 +03:00
friend void ConnectGeneratedAreas ( ) ;
friend void MergeGeneratedAreas ( ) ;
friend void MarkJumpAreas ( ) ;
2015-08-02 20:45:57 +03:00
friend bool SaveNavigationMap ( const char * filename ) ;
2016-02-04 03:18:26 +03:00
friend NavErrorType LoadNavigationMap ( ) ;
friend void DestroyNavigationMap ( ) ;
friend void DestroyHidingSpots ( ) ;
friend void StripNavigationAreas ( ) ;
2015-08-02 20:45:57 +03:00
friend class CNavAreaGrid ;
friend class CCSBotManager ;
2017-10-19 20:12:02 +03:00
void Initialize ( ) ; // to keep constructors consistent
static bool m_isReset ; // if true, don't bother cleaning up in destructor since everything is going away
static unsigned int m_nextID ; // used to allocate unique IDs
unsigned int m_id ; // unique area ID
Extent m_extent ; // extents of area in world coords (NOTE: lo.z is not necessarily the minimum Z, but corresponds to Z at point (lo.x, lo.y), etc
Vector m_center ; // centroid of area
unsigned char m_attributeFlags ; // set of attribute bit flags (see NavAttributeType)
Place m_place ; // place descriptor
2015-08-02 20:45:57 +03:00
2015-12-22 21:07:49 +03:00
// height of the implicit corners
2015-08-02 20:45:57 +03:00
float m_neZ ;
float m_swZ ;
enum { MAX_AREA_TEAMS = 2 } ;
2015-12-22 21:07:49 +03:00
// for hunting
float m_clearedTimestamp [ MAX_AREA_TEAMS ] ; // time this area was last "cleared" of enemies
// danger
2017-10-12 17:50:56 +03:00
float m_danger [ MAX_AREA_TEAMS ] ; // danger of this area, allowing bots to avoid areas where they died in the past - zero is no danger
2015-12-22 21:07:49 +03:00
float m_dangerTimestamp [ MAX_AREA_TEAMS ] ; // time when danger value was set - used for decaying
2016-02-04 03:18:26 +03:00
void DecayDanger ( ) ;
2015-08-02 20:45:57 +03:00
2015-12-22 21:07:49 +03:00
// hiding spots
2015-08-02 20:45:57 +03:00
HidingSpotList m_hidingSpotList ;
2015-12-22 21:07:49 +03:00
bool IsHidingSpotCollision ( const Vector * pos ) const ; // returns true if an existing hiding spot is too close to given position
2015-08-02 20:45:57 +03:00
2015-12-22 21:07:49 +03:00
// encounter spots
SpotEncounterList m_spotEncounterList ; // list of possible ways to move thru this area, and the spots to look at as we do
void AddSpotEncounters ( const CNavArea * from , NavDirType fromDir , const CNavArea * to , NavDirType toDir ) ;
2015-08-02 20:45:57 +03:00
2015-12-22 21:07:49 +03:00
// approach areas
2015-08-02 20:45:57 +03:00
enum { MAX_APPROACH_AREAS = 16 } ;
2015-12-22 21:07:49 +03:00
ApproachInfo m_approach [ MAX_APPROACH_AREAS ] ;
2015-08-02 20:45:57 +03:00
unsigned char m_approachCount ;
2016-02-04 03:18:26 +03:00
void Strip ( ) ; // remove "analyzed" data from nav area
2015-08-02 20:45:57 +03:00
2015-12-22 21:07:49 +03:00
// A* pathfinding algorithm
2017-10-19 20:12:02 +03:00
static unsigned int m_masterMarker ;
2017-10-12 17:50:56 +03:00
unsigned int m_marker ; // used to flag the area as visited
2015-12-22 21:07:49 +03:00
CNavArea * m_parent ; // the area just prior to this on in the search path
2017-10-12 17:50:56 +03:00
NavTraverseType m_parentHow ; // how we get from parent to us
2015-12-22 21:07:49 +03:00
float m_totalCost ; // the distance so far plus an estimate of the distance left
float m_costSoFar ; // distance travelled so far
2015-08-02 20:45:57 +03:00
2017-10-19 20:12:02 +03:00
static CNavArea * m_openList ;
2017-10-12 17:50:56 +03:00
CNavArea * m_nextOpen , * m_prevOpen ; // only valid if m_openMarker == m_masterMarker
2015-12-22 21:07:49 +03:00
unsigned int m_openMarker ; // if this equals the current marker value, we are on the open list
2015-08-02 20:45:57 +03:00
2015-12-22 21:07:49 +03:00
// connections to adjacent areas
2017-10-12 17:50:56 +03:00
NavConnectList m_connect [ NUM_DIRECTIONS ] ; // a list of adjacent areas for each direction
NavLadderList m_ladder [ NUM_LADDER_DIRECTIONS ] ; // list of ladders leading up and down from this area
2015-08-02 20:45:57 +03:00
2017-10-12 17:50:56 +03:00
CNavNode * m_node [ NUM_CORNERS ] ; // nav nodes at each corner of the area
2015-08-02 20:45:57 +03:00
2017-10-12 17:50:56 +03:00
void FinishMerge ( CNavArea * adjArea ) ; // recompute internal data once nodes have been adjusted during merge
void MergeAdjacentConnections ( CNavArea * adjArea ) ; // for merging with "adjArea" - pick up all of "adjArea"s connections
2015-12-22 21:07:49 +03:00
void AssignNodes ( CNavArea * area ) ; // assign internal nodes to the given area
void FinishSplitEdit ( CNavArea * newArea , NavDirType ignoreEdge ) ; // given the portion of the original area, update its internal data
2015-07-02 00:22:46 +03:00
2017-10-12 17:50:56 +03:00
NavAreaList m_overlapList ; // list of areas that overlap this area
void OnDestroyNotify ( CNavArea * dead ) ; // invoked when given area is going away
2015-08-02 20:45:57 +03:00
2015-12-22 21:07:49 +03:00
CNavArea * m_prevHash , * m_nextHash ; // for hash table in CNavAreaGrid
2016-02-04 03:18:26 +03:00
} ;
2015-08-02 20:45:57 +03:00
extern NavAreaList TheNavAreaList ;
2016-02-04 03:18:26 +03:00
inline bool CNavArea : : IsDegenerate ( ) const
2015-08-02 20:45:57 +03:00
{
return ( m_extent . lo . x > = m_extent . hi . x | | m_extent . lo . y > = m_extent . hi . y ) ;
}
inline CNavArea * CNavArea : : GetAdjacentArea ( NavDirType dir , int i ) const
{
2017-10-12 17:50:56 +03:00
for ( auto & con : m_connect [ dir ] )
2015-08-02 20:45:57 +03:00
{
if ( i = = 0 )
2017-10-12 17:50:56 +03:00
return con . area ;
i - - ;
2015-08-02 20:45:57 +03:00
}
2016-01-19 14:54:31 +03:00
2017-10-12 17:50:56 +03:00
return nullptr ;
2015-08-02 20:45:57 +03:00
}
2016-02-04 03:18:26 +03:00
inline bool CNavArea : : IsOpen ( ) const
2015-08-02 20:45:57 +03:00
{
2017-10-19 20:12:02 +03:00
return ( m_openMarker = = m_masterMarker ) ? true : false ;
2015-08-02 20:45:57 +03:00
}
2016-02-04 03:18:26 +03:00
inline bool CNavArea : : IsOpenListEmpty ( )
2015-08-02 20:45:57 +03:00
{
2017-10-19 20:12:02 +03:00
return m_openList ? false : true ;
2015-08-02 20:45:57 +03:00
}
2016-02-04 03:18:26 +03:00
inline CNavArea * CNavArea : : PopOpenList ( )
2015-08-02 20:45:57 +03:00
{
2017-10-19 20:12:02 +03:00
if ( m_openList )
2015-08-02 20:45:57 +03:00
{
2017-10-19 20:12:02 +03:00
CNavArea * area = m_openList ;
2015-12-22 21:07:49 +03:00
// disconnect from list
2015-08-02 20:45:57 +03:00
area - > RemoveFromOpenList ( ) ;
return area ;
}
2015-12-22 21:07:49 +03:00
2017-10-12 17:50:56 +03:00
return nullptr ;
2015-08-02 20:45:57 +03:00
}
2016-02-04 03:18:26 +03:00
inline bool CNavArea : : IsClosed ( ) const
2015-08-02 20:45:57 +03:00
{
if ( IsMarked ( ) & & ! IsOpen ( ) )
return true ;
return false ;
}
2016-02-04 03:18:26 +03:00
inline void CNavArea : : AddToClosedList ( )
2015-08-02 20:45:57 +03:00
{
Mark ( ) ;
}
2016-02-04 03:18:26 +03:00
inline void CNavArea : : RemoveFromClosedList ( )
2015-08-02 20:45:57 +03:00
{
2015-12-22 21:07:49 +03:00
// since "closed" is defined as visited (marked) and not on open list, do nothing
2015-08-02 20:45:57 +03:00
}
2015-12-22 21:07:49 +03:00
// The CNavAreaGrid is used to efficiently access navigation areas by world position
// Each cell of the grid contains a list of areas that overlap it
// Given a world position, the corresponding grid cell is ( x/cellsize, y/cellsize )
2015-08-02 20:45:57 +03:00
class CNavAreaGrid
{
public :
2016-02-04 03:18:26 +03:00
CNavAreaGrid ( ) ;
2015-08-02 20:45:57 +03:00
~ CNavAreaGrid ( ) ;
2016-02-04 03:18:26 +03:00
void Reset ( ) ; // clear the grid to empty
2015-12-22 21:07:49 +03:00
void Initialize ( float minX , float maxX , float minY , float maxY ) ; // clear and reset the grid to the given extents
2017-10-12 17:50:56 +03:00
void AddNavArea ( CNavArea * area ) ; // add an area to the grid
void RemoveNavArea ( CNavArea * area ) ; // remove an area from the grid
2016-02-04 03:18:26 +03:00
unsigned int GetNavAreaCount ( ) const { return m_areaCount ; } // return total number of nav areas
2017-10-12 17:50:56 +03:00
CNavArea * GetNavArea ( const Vector * pos , float beneathLimt = 120.0f ) const ; // given a position, return the nav area that IsOverlapping and is *immediately* beneath it
2015-08-02 20:45:57 +03:00
CNavArea * GetNavAreaByID ( unsigned int id ) const ;
CNavArea * GetNearestNavArea ( const Vector * pos , bool anyZ = false ) const ;
2015-12-22 21:07:49 +03:00
Place GetPlace ( const Vector * pos ) const ; // return radio chatter place for given coordinate
2015-08-02 20:45:57 +03:00
private :
2015-12-22 21:07:49 +03:00
const float m_cellSize ;
NavAreaList * m_grid ;
int m_gridSizeX ;
int m_gridSizeY ;
float m_minX ;
float m_minY ;
unsigned int m_areaCount ; // total number of nav areas
2015-08-02 20:45:57 +03:00
2015-12-22 21:07:49 +03:00
enum { HASH_TABLE_SIZE = 256 } ;
2017-10-12 17:50:56 +03:00
CNavArea * m_hashTable [ HASH_TABLE_SIZE ] ; // hash table to optimize lookup by ID
2015-12-22 21:07:49 +03:00
inline int ComputeHashKey ( unsigned int id ) const // returns a hash key for the given nav area ID
2015-08-02 20:45:57 +03:00
{
return id & 0xFF ;
}
2015-12-22 21:07:49 +03:00
2015-08-02 20:45:57 +03:00
inline int WorldToGridX ( float wx ) const
{
int x = ( wx - m_minX ) / m_cellSize ;
if ( x < 0 )
x = 0 ;
else if ( x > = m_gridSizeX )
x = m_gridSizeX - 1 ;
return x ;
}
inline int WorldToGridY ( float wy ) const
{
int y = ( wy - m_minY ) / m_cellSize ;
if ( y < 0 )
y = 0 ;
else if ( y > = m_gridSizeY )
y = m_gridSizeY - 1 ;
return y ;
}
2016-02-04 03:18:26 +03:00
} ;
2015-08-02 20:45:57 +03:00
2016-12-06 22:21:52 +03:00
extern CNavAreaGrid TheNavAreaGrid ;
2015-08-02 20:45:57 +03:00
class ShortestPathCost
{
public :
2015-12-22 21:07:49 +03:00
float operator ( ) ( CNavArea * area , CNavArea * fromArea , const CNavLadder * ladder )
2015-08-02 20:45:57 +03:00
{
2017-10-12 17:50:56 +03:00
if ( ! fromArea )
2015-08-02 20:45:57 +03:00
{
2015-12-22 21:07:49 +03:00
// first area in path, no cost
2015-08-02 20:45:57 +03:00
return 0.0f ;
}
else
{
2015-12-22 21:07:49 +03:00
// compute distance travelled along path so far
2015-08-02 20:45:57 +03:00
float dist ;
if ( ladder )
dist = ladder - > m_length ;
else
dist = ( * area - > GetCenter ( ) - * fromArea - > GetCenter ( ) ) . Length ( ) ;
float cost = dist + fromArea - > GetCostSoFar ( ) ;
2015-12-22 21:07:49 +03:00
// if this is a "crouch" area, add penalty
2015-08-02 20:45:57 +03:00
if ( area - > GetAttributes ( ) & NAV_CROUCH )
{
const float crouchPenalty = 20.0f ;
cost + = crouchPenalty * dist ;
}
2015-12-22 21:07:49 +03:00
// if this is a "jump" area, add penalty
2015-08-02 20:45:57 +03:00
if ( area - > GetAttributes ( ) & NAV_JUMP )
{
const float jumpPenalty = 5.0f ;
cost + = jumpPenalty * dist ;
}
2015-12-22 21:07:49 +03:00
2015-08-02 20:45:57 +03:00
return cost ;
}
}
2015-12-22 21:07:49 +03:00
} ;
// Find path from startArea to goalArea via an A* search, using supplied cost heuristic.
// If cost functor returns -1 for an area, that area is considered a dead end.
// This doesn't actually build a path, but the path is defined by following parent
// pointers back from goalArea to startArea.
// If 'closestArea' is non-NULL, the closest area to the goal is returned (useful if the path fails).
// If 'goalArea' is NULL, will compute a path as close as possible to 'goalPos'.
// If 'goalPos' is NULL, will use the center of 'goalArea' as the goal position.
// Returns true if a path exists.
2015-08-02 20:45:57 +03:00
template < typename CostFunctor >
2017-10-12 17:50:56 +03:00
bool NavAreaBuildPath ( CNavArea * startArea , CNavArea * goalArea , const Vector * goalPos , CostFunctor & costFunc , CNavArea * * closestArea = nullptr )
2015-08-02 20:45:57 +03:00
{
2017-10-12 17:50:56 +03:00
if ( closestArea )
* closestArea = nullptr ;
2015-08-02 20:45:57 +03:00
2017-10-12 17:50:56 +03:00
if ( ! startArea )
2015-08-02 20:45:57 +03:00
return false ;
2015-12-22 21:07:49 +03:00
// If goalArea is NULL, this function will return the closest area to the goal.
// However, if there is also no goal, we can't do anything.
2017-10-12 17:50:56 +03:00
if ( ! goalArea & & ! goalPos )
2015-08-02 20:45:57 +03:00
{
return false ;
}
2017-10-12 17:50:56 +03:00
startArea - > SetParent ( nullptr ) ;
2015-12-22 21:07:49 +03:00
// if we are already in the goal area, build trivial path
2015-08-02 20:45:57 +03:00
if ( startArea = = goalArea )
{
2017-10-12 17:50:56 +03:00
goalArea - > SetParent ( nullptr ) ;
2015-08-02 20:45:57 +03:00
2017-10-12 17:50:56 +03:00
if ( closestArea )
2015-08-02 20:45:57 +03:00
* closestArea = goalArea ;
return true ;
}
2015-12-22 21:07:49 +03:00
// determine actual goal position
2017-10-12 17:50:56 +03:00
Vector actualGoalPos = ( goalPos ! = nullptr ) ? ( * goalPos ) : ( * goalArea - > GetCenter ( ) ) ;
2015-12-22 21:07:49 +03:00
// start search
2015-08-02 20:45:57 +03:00
CNavArea : : ClearSearchLists ( ) ;
2015-12-22 21:07:49 +03:00
// compute estimate of path length
// TODO: Cost might work as "manhattan distance"
2015-08-02 20:45:57 +03:00
startArea - > SetTotalCost ( ( * startArea - > GetCenter ( ) - actualGoalPos ) . Length ( ) ) ;
2017-11-22 20:27:55 +03:00
real_t initCost = costFunc ( startArea , nullptr , nullptr ) ;
2015-08-02 20:45:57 +03:00
if ( initCost < 0.0f )
return false ;
2015-12-05 22:40:30 +03:00
2015-08-02 20:45:57 +03:00
startArea - > SetCostSoFar ( initCost ) ;
startArea - > AddToOpenList ( ) ;
2015-12-22 21:07:49 +03:00
// keep track of the area we visit that is closest to the goal
2017-10-12 17:50:56 +03:00
if ( closestArea )
2015-08-02 20:45:57 +03:00
* closestArea = startArea ;
2015-12-05 22:40:30 +03:00
float closestAreaDist = startArea - > GetTotalCost ( ) ;
2015-12-22 21:07:49 +03:00
// do A* search
2015-08-02 20:45:57 +03:00
while ( ! CNavArea : : IsOpenListEmpty ( ) )
{
2015-12-22 21:07:49 +03:00
// get next area to check
2015-08-02 20:45:57 +03:00
CNavArea * area = CNavArea : : PopOpenList ( ) ;
2015-12-22 21:07:49 +03:00
// check if we have found the goal area
2015-08-02 20:45:57 +03:00
if ( area = = goalArea )
{
2017-10-12 17:50:56 +03:00
if ( closestArea )
2015-08-02 20:45:57 +03:00
* closestArea = goalArea ;
return true ;
}
2015-12-22 21:07:49 +03:00
// search adjacent areas
2015-08-02 20:45:57 +03:00
bool searchFloor = true ;
int dir = NORTH ;
const NavConnectList * floorList = area - > GetAdjacentList ( NORTH ) ;
2017-10-12 17:50:56 +03:00
auto floorIter = floorList - > begin ( ) ;
2015-08-02 20:45:57 +03:00
bool ladderUp = true ;
2017-10-12 17:50:56 +03:00
const NavLadderList * ladderList = nullptr ;
2015-08-02 20:45:57 +03:00
NavLadderList : : const_iterator ladderIter ;
enum
{
AHEAD = 0 ,
LEFT ,
RIGHT ,
BEHIND ,
NUM_TOP_DIRECTIONS
} ;
2015-12-05 22:40:30 +03:00
int ladderTopDir ;
2015-08-02 20:45:57 +03:00
while ( true )
{
CNavArea * newArea ;
NavTraverseType how ;
2017-10-12 17:50:56 +03:00
const CNavLadder * ladder = nullptr ;
2015-08-02 20:45:57 +03:00
2015-12-22 21:07:49 +03:00
// Get next adjacent area - either on floor or via ladder
2015-08-02 20:45:57 +03:00
if ( searchFloor )
{
2015-12-22 21:07:49 +03:00
// if exhausted adjacent connections in current direction, begin checking next direction
2015-08-02 20:45:57 +03:00
if ( floorIter = = floorList - > end ( ) )
{
2017-10-12 17:50:56 +03:00
dir + + ;
2015-08-02 20:45:57 +03:00
if ( dir = = NUM_DIRECTIONS )
{
2015-12-22 21:07:49 +03:00
// checked all directions on floor - check ladders next
2015-08-02 20:45:57 +03:00
searchFloor = false ;
ladderList = area - > GetLadderList ( LADDER_UP ) ;
ladderIter = ladderList - > begin ( ) ;
ladderTopDir = AHEAD ;
}
else
{
2015-12-22 21:07:49 +03:00
// start next direction
2015-08-02 20:45:57 +03:00
floorList = area - > GetAdjacentList ( ( NavDirType ) dir ) ;
floorIter = floorList - > begin ( ) ;
}
continue ;
}
newArea = ( * floorIter ) . area ;
how = ( NavTraverseType ) dir ;
2017-10-12 17:50:56 +03:00
floorIter + + ;
2015-08-02 20:45:57 +03:00
}
2015-12-22 21:07:49 +03:00
// search ladders
2015-08-02 20:45:57 +03:00
else
{
if ( ladderIter = = ladderList - > end ( ) )
{
if ( ! ladderUp )
2015-12-22 21:07:49 +03:00
{
// checked both ladder directions - done
2015-08-02 20:45:57 +03:00
break ;
2015-12-22 21:07:49 +03:00
}
2015-08-02 20:45:57 +03:00
else
{
2015-12-22 21:07:49 +03:00
// check down ladders
2015-08-02 20:45:57 +03:00
ladderUp = false ;
ladderList = area - > GetLadderList ( LADDER_DOWN ) ;
ladderIter = ladderList - > begin ( ) ;
}
continue ;
}
if ( ladderUp )
{
2015-12-09 01:39:54 +03:00
ladder = ( * ladderIter ) ;
2015-12-22 21:07:49 +03:00
// cannot use this ladder if the ladder bottom is hanging above our head
2015-08-02 20:45:57 +03:00
if ( ladder - > m_isDangling )
{
2017-10-12 17:50:56 +03:00
ladderIter + + ;
2015-08-02 20:45:57 +03:00
continue ;
}
2015-12-22 21:07:49 +03:00
// do not use BEHIND connection, as its very hard to get to when going up a ladder
2015-08-02 20:45:57 +03:00
if ( ladderTopDir = = AHEAD )
newArea = ladder - > m_topForwardArea ;
else if ( ladderTopDir = = LEFT )
newArea = ladder - > m_topLeftArea ;
else if ( ladderTopDir = = RIGHT )
newArea = ladder - > m_topRightArea ;
else
{
2017-10-12 17:50:56 +03:00
ladderIter + + ;
2015-08-02 20:45:57 +03:00
continue ;
}
how = GO_LADDER_UP ;
2017-10-12 17:50:56 +03:00
ladderTopDir + + ;
2015-08-02 20:45:57 +03:00
}
else
{
newArea = ( * ladderIter ) - > m_bottomArea ;
how = GO_LADDER_DOWN ;
ladder = ( * ladderIter ) ;
2017-10-12 17:50:56 +03:00
ladderIter + + ;
2015-08-02 20:45:57 +03:00
}
2017-10-12 17:50:56 +03:00
if ( ! newArea )
2015-08-02 20:45:57 +03:00
continue ;
}
2015-12-22 21:07:49 +03:00
// don't backtrack
2015-08-02 20:45:57 +03:00
if ( newArea = = area )
continue ;
2017-11-22 20:27:55 +03:00
real_t newCostSoFar = costFunc ( newArea , area , ladder ) ;
2015-12-22 21:07:49 +03:00
// check if cost functor says this area is a dead-end
2015-08-02 20:45:57 +03:00
if ( newCostSoFar < 0.0f )
continue ;
if ( ( newArea - > IsOpen ( ) | | newArea - > IsClosed ( ) ) & & newArea - > GetCostSoFar ( ) < = newCostSoFar )
2015-12-22 21:07:49 +03:00
{
// this is a worse path - skip it
2015-08-02 20:45:57 +03:00
continue ;
2015-12-22 21:07:49 +03:00
}
2015-08-02 20:45:57 +03:00
else
{
2015-12-22 21:07:49 +03:00
// compute estimate of distance left to go
2017-11-22 20:27:55 +03:00
real_t newCostRemaining = ( * newArea - > GetCenter ( ) - actualGoalPos ) . Length ( ) ;
2015-12-22 21:07:49 +03:00
// track closest area to goal in case path fails
2017-10-12 17:50:56 +03:00
if ( closestArea & & newCostRemaining < closestAreaDist )
2015-08-02 20:45:57 +03:00
{
* closestArea = newArea ;
closestAreaDist = newCostRemaining ;
}
newArea - > SetParent ( area , how ) ;
newArea - > SetCostSoFar ( newCostSoFar ) ;
newArea - > SetTotalCost ( newCostSoFar + newCostRemaining ) ;
if ( newArea - > IsClosed ( ) )
newArea - > RemoveFromClosedList ( ) ;
if ( newArea - > IsOpen ( ) )
2015-12-22 21:07:49 +03:00
{
// area already on open list, update the list order to keep costs sorted
2015-08-02 20:45:57 +03:00
newArea - > UpdateOnOpenList ( ) ;
2015-12-22 21:07:49 +03:00
}
2015-08-02 20:45:57 +03:00
else
newArea - > AddToOpenList ( ) ;
}
}
2015-12-05 22:40:30 +03:00
2015-12-22 21:07:49 +03:00
// we have searched this area
2015-08-02 20:45:57 +03:00
area - > AddToClosedList ( ) ;
}
2015-12-05 22:40:30 +03:00
2015-08-02 20:45:57 +03:00
return false ;
}
2015-12-22 21:07:49 +03:00
// Compute distance between two areas. Return -1 if can't reach 'endArea' from 'startArea'.
2015-08-02 20:45:57 +03:00
template < typename CostFunctor >
2017-11-22 20:27:55 +03:00
real_t NavAreaTravelDistance ( CNavArea * startArea , CNavArea * endArea , CostFunctor & costFunc )
2015-08-02 20:45:57 +03:00
{
2017-10-12 17:50:56 +03:00
if ( ! startArea )
2015-08-02 20:45:57 +03:00
return - 1.0f ;
2017-10-12 17:50:56 +03:00
if ( ! endArea )
2015-08-02 20:45:57 +03:00
return - 1.0f ;
if ( startArea = = endArea )
return 0.0f ;
2015-12-22 21:07:49 +03:00
// compute path between areas using given cost heuristic
2017-10-12 17:50:56 +03:00
if ( NavAreaBuildPath ( startArea , endArea , nullptr , costFunc ) = = false )
2015-08-02 20:45:57 +03:00
return - 1.0f ;
2015-12-22 21:07:49 +03:00
// compute distance along path
2017-11-22 20:27:55 +03:00
real_t distance = 0.0f ;
2015-08-02 20:45:57 +03:00
for ( CNavArea * area = endArea ; area - > GetParent ( ) ; area = area - > GetParent ( ) )
{
distance + = ( * area - > GetCenter ( ) - * area - > GetParent ( ) - > GetCenter ( ) ) . Length ( ) ;
}
2015-12-22 21:07:49 +03:00
2015-08-02 20:45:57 +03:00
return distance ;
}
2015-12-22 21:07:49 +03:00
// Compute distance from area to position. Return -1 if can't reach position.
2015-08-02 20:45:57 +03:00
template < typename CostFunctor >
float NavAreaTravelDistance ( const Vector * startPos , CNavArea * startArea , const Vector * goalPos , CostFunctor & costFunc )
{
2017-10-12 17:50:56 +03:00
if ( ! startArea | | ! startPos | | ! goalPos )
2015-08-02 20:45:57 +03:00
return - 1.0f ;
2015-12-22 21:07:49 +03:00
// compute path between areas using given cost heuristic
2017-11-22 20:27:55 +03:00
CNavArea * goalArea = nullptr ;
2015-08-02 20:45:57 +03:00
if ( NavAreaBuildPath ( startArea , TheNavAreaGrid . GetNearestNavArea ( goalPos ) , goalPos , costFunc , & goalArea ) = = false )
return - 1.0f ;
2017-10-12 17:50:56 +03:00
if ( ! goalArea )
2015-08-02 20:45:57 +03:00
return - 1.0f ;
2015-12-22 21:07:49 +03:00
// compute distance along path
2017-10-12 17:50:56 +03:00
if ( ! goalArea - > GetParent ( ) )
2015-12-22 21:07:49 +03:00
{
2015-08-02 20:45:57 +03:00
return ( * goalPos - * startPos ) . Length ( ) ;
2015-12-22 21:07:49 +03:00
}
2015-08-02 20:45:57 +03:00
else
{
CNavArea * area = goalArea - > GetParent ( ) ;
float distance = ( * goalPos - * area - > GetCenter ( ) ) . Length ( ) ;
for ( ; area - > GetParent ( ) ; area = area - > GetParent ( ) )
{
distance + = ( * area - > GetCenter ( ) - * area - > GetParent ( ) - > GetCenter ( ) ) . Length ( ) ;
}
2015-12-22 21:07:49 +03:00
2015-08-02 20:45:57 +03:00
return distance ;
}
}
2015-12-22 21:07:49 +03:00
// Do a breadth-first search, invoking functor on each area.
// If functor returns 'true', continue searching from this area.
// If functor returns 'false', the area's adjacent areas are not explored (dead end).
// If 'maxRange' is 0 or less, no range check is done (all areas will be examined).
// NOTE: Returns all areas that overlap range, even partially
// TODO: Use ladder connections
2016-02-23 02:13:52 +03:00
//
2015-12-22 21:07:49 +03:00
// helper function
2015-08-02 20:45:57 +03:00
inline void AddAreaToOpenList ( CNavArea * area , CNavArea * parent , const Vector * startPos , float maxRange )
{
2017-10-12 17:50:56 +03:00
if ( ! area )
2015-08-02 20:45:57 +03:00
return ;
if ( ! area - > IsMarked ( ) )
{
area - > Mark ( ) ;
area - > SetTotalCost ( 0.0f ) ;
area - > SetParent ( parent ) ;
if ( maxRange > 0.0f )
{
2015-12-22 21:07:49 +03:00
// make sure this area overlaps range
2015-08-02 20:45:57 +03:00
Vector closePos ;
area - > GetClosestPointOnArea ( startPos , & closePos ) ;
if ( ( closePos - * startPos ) . Make2D ( ) . IsLengthLessThan ( maxRange ) )
{
2015-12-22 21:07:49 +03:00
// compute approximate distance along path to limit travel range, too
2015-08-02 20:45:57 +03:00
float distAlong = parent - > GetCostSoFar ( ) ;
distAlong + = ( * area - > GetCenter ( ) - * parent - > GetCenter ( ) ) . Length ( ) ;
area - > SetCostSoFar ( distAlong ) ;
2015-12-22 21:07:49 +03:00
// allow for some fudge due to large size areas
2015-08-02 20:45:57 +03:00
if ( distAlong < = 1.5f * maxRange )
area - > AddToOpenList ( ) ;
}
}
else
2015-12-22 21:07:49 +03:00
{
// infinite range
2015-08-02 20:45:57 +03:00
area - > AddToOpenList ( ) ;
2015-12-22 21:07:49 +03:00
}
2015-08-02 20:45:57 +03:00
}
}
template < typename Functor >
void SearchSurroundingAreas ( CNavArea * startArea , const Vector * startPos , Functor & func , float maxRange = - 1.0f )
{
2017-10-12 17:50:56 +03:00
if ( ! startArea | | ! startPos )
2015-08-02 20:45:57 +03:00
return ;
CNavArea : : MakeNewMarker ( ) ;
CNavArea : : ClearSearchLists ( ) ;
startArea - > AddToOpenList ( ) ;
startArea - > SetTotalCost ( 0.0f ) ;
startArea - > SetCostSoFar ( 0.0f ) ;
2017-10-12 17:50:56 +03:00
startArea - > SetParent ( nullptr ) ;
2015-08-02 20:45:57 +03:00
startArea - > Mark ( ) ;
while ( ! CNavArea : : IsOpenListEmpty ( ) )
{
2015-12-05 22:40:30 +03:00
// get next area to check
2015-08-02 20:45:57 +03:00
CNavArea * area = CNavArea : : PopOpenList ( ) ;
2015-12-05 22:40:30 +03:00
// invoke functor on area
2015-08-02 20:45:57 +03:00
if ( func ( area ) )
{
2015-12-05 22:40:30 +03:00
// explore adjacent floor areas
2017-10-12 17:50:56 +03:00
for ( int dir = 0 ; dir < NUM_DIRECTIONS ; dir + + )
2015-08-02 20:45:57 +03:00
{
int count = area - > GetAdjacentCount ( ( NavDirType ) dir ) ;
2017-10-12 17:50:56 +03:00
for ( int i = 0 ; i < count ; i + + )
2015-08-02 20:45:57 +03:00
{
CNavArea * adjArea = area - > GetAdjacentArea ( ( NavDirType ) dir , i ) ;
AddAreaToOpenList ( adjArea , area , startPos , maxRange ) ;
}
}
2015-12-05 22:40:30 +03:00
// explore adjacent areas connected by ladders
2015-08-02 20:45:57 +03:00
NavLadderList : : const_iterator ladderIt ;
2015-12-05 22:40:30 +03:00
// check up ladders
2015-08-02 20:45:57 +03:00
const NavLadderList * ladderList = area - > GetLadderList ( LADDER_UP ) ;
2017-10-12 17:50:56 +03:00
if ( ladderList )
2015-08-02 20:45:57 +03:00
{
2017-10-12 17:50:56 +03:00
for ( ladderIt = ladderList - > begin ( ) ; ladderIt ! = ladderList - > end ( ) ; ladderIt + + )
2015-08-02 20:45:57 +03:00
{
const CNavLadder * ladder = * ladderIt ;
2015-12-05 22:40:30 +03:00
// cannot use this ladder if the ladder bottom is hanging above our head
2015-08-02 20:45:57 +03:00
if ( ladder - > m_isDangling )
{
continue ;
}
2015-12-05 22:40:30 +03:00
// do not use BEHIND connection, as its very hard to get to when going up a ladder
2015-08-02 20:45:57 +03:00
AddAreaToOpenList ( ladder - > m_topForwardArea , area , startPos , maxRange ) ;
AddAreaToOpenList ( ladder - > m_topLeftArea , area , startPos , maxRange ) ;
AddAreaToOpenList ( ladder - > m_topRightArea , area , startPos , maxRange ) ;
}
}
2015-12-05 22:40:30 +03:00
// check down ladders
2015-08-02 20:45:57 +03:00
ladderList = area - > GetLadderList ( LADDER_DOWN ) ;
2017-10-12 17:50:56 +03:00
if ( ladderList )
2015-08-02 20:45:57 +03:00
{
2017-10-12 17:50:56 +03:00
for ( ladderIt = ladderList - > begin ( ) ; ladderIt ! = ladderList - > end ( ) ; ladderIt + + )
2015-08-02 20:45:57 +03:00
{
const CNavLadder * ladder = * ladderIt ;
AddAreaToOpenList ( ladder - > m_bottomArea , area , startPos , maxRange ) ;
}
}
}
}
}
2015-12-22 21:07:49 +03:00
// Apply the functor to all navigation areas
2016-01-19 14:54:31 +03:00
// If functor returns false, stop processing and return false.
2015-08-02 20:45:57 +03:00
template < typename Functor >
2016-01-19 14:54:31 +03:00
bool ForAllAreas ( Functor & func )
2015-08-02 20:45:57 +03:00
{
NavAreaList : : iterator iter ;
2017-10-12 17:50:56 +03:00
for ( iter = TheNavAreaList . begin ( ) ; iter ! = TheNavAreaList . end ( ) ; iter + + )
2015-08-02 20:45:57 +03:00
{
2016-02-23 02:13:52 +03:00
CNavArea * area = ( * iter ) ;
2016-01-19 14:54:31 +03:00
if ( func ( area ) = = false )
return false ;
2015-08-02 20:45:57 +03:00
}
2016-01-19 14:54:31 +03:00
return true ;
2015-08-02 20:45:57 +03:00
}
2015-12-22 21:07:49 +03:00
// Fuctor that returns lowest cost for farthest away areas
// For use with FindMinimumCostArea()
2016-02-23 02:13:52 +03:00
class FarAwayFunctor
2015-12-22 21:07:49 +03:00
{
public :
float operator ( ) ( CNavArea * area , CNavArea * fromArea , const CNavLadder * ladder )
{
if ( area = = fromArea )
return 9999999.9f ;
2016-01-19 14:54:31 +03:00
return 1.0f / ( * fromArea - > GetCenter ( ) - * area - > GetCenter ( ) ) . Length ( ) ;
2015-12-22 21:07:49 +03:00
}
} ;
// Fuctor that returns lowest cost for farthest away areas
// For use with FindMinimumCostArea()
2015-08-02 20:45:57 +03:00
class FarAwayFromPositionFunctor
{
public :
2016-02-04 03:18:26 +03:00
FarAwayFromPositionFunctor ( const Vector * pos ) { m_pos = pos ; }
2015-08-02 20:45:57 +03:00
float operator ( ) ( CNavArea * area , CNavArea * fromArea , const CNavLadder * ladder )
{
return 1.0f / ( * m_pos - * area - > GetCenter ( ) ) . Length ( ) ;
}
private :
const Vector * m_pos ;
2016-02-04 03:18:26 +03:00
} ;
2015-08-02 20:45:57 +03:00
2015-12-22 21:07:49 +03:00
// Pick a low-cost area of "decent" size
2015-08-02 20:45:57 +03:00
template < typename CostFunctor >
CNavArea * FindMinimumCostArea ( CNavArea * startArea , CostFunctor & costFunc )
{
const float minSize = 150.0f ;
2015-12-22 21:07:49 +03:00
// collect N low-cost areas of a decent size
2015-08-02 20:45:57 +03:00
enum { NUM_CHEAP_AREAS = 32 } ;
struct
{
CNavArea * area ;
float cost ;
}
cheapAreaSet [ NUM_CHEAP_AREAS ] ;
int cheapAreaSetCount = 0 ;
NavAreaList : : iterator iter ;
2017-10-12 17:50:56 +03:00
for ( iter = TheNavAreaList . begin ( ) ; iter ! = TheNavAreaList . end ( ) ; iter + + )
2015-08-02 20:45:57 +03:00
{
2016-02-23 02:13:52 +03:00
CNavArea * area = ( * iter ) ;
2015-12-22 21:07:49 +03:00
// skip the small areas
2015-08-02 20:45:57 +03:00
const Extent * extent = area - > GetExtent ( ) ;
if ( extent - > hi . x - extent - > lo . x < minSize | | extent - > hi . y - extent - > lo . y < minSize )
continue ;
2015-12-22 21:07:49 +03:00
// compute cost of this area
2017-10-12 17:50:56 +03:00
float cost = costFunc ( area , startArea , nullptr ) ;
2015-12-22 21:07:49 +03:00
2015-08-02 20:45:57 +03:00
if ( cheapAreaSetCount < NUM_CHEAP_AREAS )
{
cheapAreaSet [ cheapAreaSetCount ] . area = area ;
cheapAreaSet [ cheapAreaSetCount + + ] . cost = cost ;
}
else
{
2015-12-22 21:07:49 +03:00
// replace most expensive cost if this is cheaper
2015-08-02 20:45:57 +03:00
int expensive = 0 ;
2017-10-12 17:50:56 +03:00
for ( int i = 1 ; i < NUM_CHEAP_AREAS ; i + + )
2016-01-19 14:54:31 +03:00
{
2015-08-02 20:45:57 +03:00
if ( cheapAreaSet [ i ] . cost > cheapAreaSet [ expensive ] . cost )
expensive = i ;
2016-01-19 14:54:31 +03:00
}
2015-08-02 20:45:57 +03:00
if ( cheapAreaSet [ expensive ] . cost > cost )
{
cheapAreaSet [ expensive ] . area = area ;
cheapAreaSet [ expensive ] . cost = cost ;
}
}
}
if ( cheapAreaSetCount )
{
2015-12-22 21:07:49 +03:00
// pick one of the areas at random
return cheapAreaSet [ RANDOM_LONG ( 0 , cheapAreaSetCount - 1 ) ] . area ;
2015-08-02 20:45:57 +03:00
}
else
{
2015-12-22 21:07:49 +03:00
// degenerate case - no decent sized areas - pick a random area
2015-08-02 20:45:57 +03:00
int numAreas = TheNavAreaList . size ( ) ;
2015-12-22 21:07:49 +03:00
int which = RANDOM_LONG ( 0 , numAreas - 1 ) ;
2015-08-02 20:45:57 +03:00
NavAreaList : : iterator iter ;
2017-10-12 17:50:56 +03:00
for ( iter = TheNavAreaList . begin ( ) ; iter ! = TheNavAreaList . end ( ) ; iter + + )
2016-01-19 14:54:31 +03:00
{
2015-08-02 20:45:57 +03:00
if ( which - - = = 0 )
break ;
2016-01-19 14:54:31 +03:00
}
2015-08-02 20:45:57 +03:00
2016-02-23 02:13:52 +03:00
return ( * iter ) ;
2015-08-02 20:45:57 +03:00
}
}
2015-12-22 21:07:49 +03:00
bool IsHidingSpotInCover ( const Vector * spot ) ;
void ClassifySniperSpot ( HidingSpot * spot ) ;
2016-02-04 03:18:26 +03:00
void DestroyHidingSpots ( ) ;
2015-12-22 21:07:49 +03:00
void EditNavAreas ( NavEditCmdType cmd ) ;
2017-10-12 17:50:56 +03:00
bool GetGroundHeight ( const Vector * pos , float * height , Vector * normal = nullptr ) ;
bool GetSimpleGroundHeight ( const Vector * pos , float * height , Vector * normal = nullptr ) ;
2016-02-04 03:18:26 +03:00
CNavArea * GetMarkedArea ( ) ;
void EditNavAreasReset ( ) ;
2015-12-22 21:07:49 +03:00
void DrawHidingSpots ( const CNavArea * area ) ;
void IncreaseDangerNearby ( int teamID , float amount , CNavArea * startArea , const Vector * pos , float maxRadius ) ;
2016-02-04 03:18:26 +03:00
void DrawDanger ( ) ;
2015-12-22 21:07:49 +03:00
bool IsSpotOccupied ( CBaseEntity * me , const Vector * pos ) ; // if a player is at the given spot, return true
const Vector * FindNearbyHidingSpot ( CBaseEntity * me , const Vector * pos , CNavArea * startArea , float maxRange = 1000.0f , bool isSniper = false , bool useNearest = false ) ;
const Vector * FindNearbyRetreatSpot ( CBaseEntity * me , const Vector * start , CNavArea * startArea , float maxRange = 1000.0f , int avoidTeam = 0 , bool useCrouchAreas = true ) ;
// return true if moving from "start" to "finish" will cross a player's line of fire
2017-10-12 17:50:56 +03:00
bool IsCrossingLineOfFire ( const Vector & start , const Vector & finish , CBaseEntity * ignore = nullptr , int ignoreTeam = 0 ) ;
2015-12-22 21:07:49 +03:00
const Vector * FindRandomHidingSpot ( CBaseEntity * me , Place place , bool isSniper = false ) ;
HidingSpot * GetHidingSpotByID ( unsigned int id ) ;
2016-02-04 03:18:26 +03:00
void ApproachAreaAnalysisPrep ( ) ;
void CleanupApproachAreaAnalysisPrep ( ) ;
void DestroyLadders ( ) ;
void DestroyNavigationMap ( ) ;
void StripNavigationAreas ( ) ;
2015-12-22 21:07:49 +03:00
CNavArea * FindFirstAreaInDirection ( const Vector * start , NavDirType dir , float range , float beneathLimit , CBaseEntity * traceIgnore , Vector * closePos ) ;
bool testJumpDown ( const Vector * fromPos , const Vector * toPos ) ;
2016-02-04 03:18:26 +03:00
void ConnectGeneratedAreas ( ) ;
void MergeGeneratedAreas ( ) ;
2015-12-22 21:07:49 +03:00
bool IsAreaRoughlySquare ( const CNavArea * area ) ;
void SplitX ( CNavArea * area ) ;
void SplitY ( CNavArea * area ) ;
2016-02-04 03:18:26 +03:00
void SquareUpAreas ( ) ;
2015-12-22 21:07:49 +03:00
bool TestArea ( CNavNode * node , int width , int height ) ;
int BuildArea ( CNavNode * node , int width , int height ) ;
2016-02-04 03:18:26 +03:00
void BuildLadders ( ) ;
void MarkJumpAreas ( ) ;
void GenerateNavigationAreaMesh ( ) ;