1
0
mirror of https://github.com/alliedmodders/hl2sdk.git synced 2025-01-03 16:13:22 +08:00
hl2sdk/dlls/nav_area.h

587 lines
26 KiB
C
Raw Normal View History

//========= Copyright <20> 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//
//=============================================================================//
// nav_area.h
// Navigation areas
// Author: Michael S. Booth (mike@turtlerockstudios.com), January 2003
#ifndef _NAV_AREA_H_
#define _NAV_AREA_H_
#include "nav_ladder.h"
// BOTPORT: Clean up relationship between team index and danger storage in nav areas
enum { MAX_NAV_TEAMS = 2 };
//-------------------------------------------------------------------------------------------------------------------
/**
* The NavConnect union is used to refer to connections to areas
*/
union NavConnect
{
unsigned int id;
CNavArea *area;
bool operator==( const NavConnect &other ) const
{
return (area == other.area) ? true : false;
}
};
typedef CUtlLinkedList<NavConnect, int> NavConnectList;
//-------------------------------------------------------------------------------------------------------------------
/**
* The NavLadderConnect union is used to refer to connections to ladders
*/
union NavLadderConnect
{
unsigned int id;
CNavLadder *ladder;
bool operator==( const NavLadderConnect &other ) const
{
return (ladder == other.ladder) ? true : false;
}
};
typedef CUtlLinkedList<NavLadderConnect, int> NavLadderConnectList;
//--------------------------------------------------------------------------------------------------------------
/**
* A HidingSpot is a good place for a bot to crouch and wait for enemies
*/
class HidingSpot
{
public:
virtual ~HidingSpot() { }
enum
{
IN_COVER = 0x01, ///< in a corner with good hard cover nearby
GOOD_SNIPER_SPOT = 0x02, ///< had at least one decent sniping corridor
IDEAL_SNIPER_SPOT = 0x04, ///< can see either very far, or a large area, or both
EXPOSED = 0x08 ///< spot in the open, usually on a ledge or cliff
};
bool HasGoodCover( void ) const { return (m_flags & IN_COVER) ? true : false; } ///< return true if hiding spot in in cover
bool IsGoodSniperSpot( void ) const { return (m_flags & GOOD_SNIPER_SPOT) ? true : false; }
bool IsIdealSniperSpot( void ) const { return (m_flags & IDEAL_SNIPER_SPOT) ? true : false; }
bool IsExposed( void ) const { return (m_flags & EXPOSED) ? true : false; }
int GetFlags( void ) const { return m_flags; }
void Save( FileHandle_t file, unsigned int version ) const;
void Load( FileHandle_t file, unsigned int version );
NavErrorType PostLoad( void );
const Vector &GetPosition( void ) const { return m_pos; } ///< get the position of the hiding spot
unsigned int GetID( void ) const { return m_id; }
const CNavArea *GetArea( void ) const { return m_area; } ///< return nav area this hiding spot is within
void Mark( void ) { m_marker = m_masterMarker; }
bool IsMarked( void ) const { return (m_marker == m_masterMarker) ? true : false; }
static void ChangeMasterMarker( void ) { ++m_masterMarker; }
public:
void SetFlags( int flags ) { m_flags |= flags; } ///< FOR INTERNAL USE ONLY
void SetPosition( const Vector &pos ) { m_pos = pos; } ///< FOR INTERNAL USE ONLY
private:
friend class CNavMesh;
friend void ClassifySniperSpot( HidingSpot *spot );
HidingSpot( void ); ///< must use factory to create
Vector m_pos; ///< world coordinates of the spot
unsigned int m_id; ///< this spot's unique ID
unsigned int m_marker; ///< this spot's unique marker
CNavArea *m_area; ///< the nav area containing this hiding spot
unsigned char m_flags; ///< bit flags
static unsigned int m_nextID; ///< used when allocating spot ID's
static unsigned int m_masterMarker; ///< used to mark spots
};
typedef CUtlLinkedList< HidingSpot *, int > HidingSpotList;
extern HidingSpotList TheHidingSpotList;
extern HidingSpot *GetHidingSpotByID( unsigned int id );
//--------------------------------------------------------------------------------------------------------------
/**
* Stores a pointer to an interesting "spot", and a parametric distance along a path
*/
struct SpotOrder
{
float t; ///< parametric distance along ray where this spot first has LOS to our path
union
{
HidingSpot *spot; ///< the spot to look at
unsigned int id; ///< spot ID for save/load
};
};
typedef CUtlLinkedList< SpotOrder, int > SpotOrderList;
/**
* This struct stores possible path segments thru a CNavArea, and the dangerous spots
* to look at as we traverse that path segment.
*/
struct SpotEncounter
{
NavConnect from;
NavDirType fromDir;
NavConnect to;
NavDirType toDir;
Ray path; ///< the path segment
SpotOrderList spotList; ///< list of spots to look at, in order of occurrence
};
typedef CUtlLinkedList< SpotEncounter *, int > SpotEncounterList;
//-------------------------------------------------------------------------------------------------------------------
/**
* A CNavArea is a rectangular region defining a walkable area in the environment
*/
class CNavArea
{
public:
CNavArea( CNavNode *nwNode, CNavNode *neNode, CNavNode *seNode, CNavNode *swNode );
CNavArea( void );
CNavArea( const Vector &corner, const Vector &otherCorner );
CNavArea( const Vector &nwCorner, const Vector &neCorner, const Vector &seCorner, const Vector &swCorner );
~CNavArea();
void ConnectTo( CNavArea *area, NavDirType dir ); ///< connect this area to given area in given direction
void Disconnect( CNavArea *area ); ///< disconnect this area from given area
void ConnectTo( CNavLadder *ladder ); ///< connect this area to given ladder
void Disconnect( CNavLadder *ladder ); ///< disconnect this area from given ladder
void Save( FileHandle_t file, unsigned int version ) const;
void Load( FileHandle_t file, unsigned int version );
NavErrorType PostLoad( void );
unsigned int GetID( void ) const { return m_id; } ///< return this area's unique ID
static void CompressIDs( void ); ///< re-orders area ID's so they are continuous
void SetAttributes( int bits ) { m_attributeFlags = (unsigned short)bits; }
int GetAttributes( void ) const { return m_attributeFlags; }
void SetPlace( Place place ) { m_place = place; } ///< set place descriptor
Place GetPlace( void ) const { return m_place; } ///< get place descriptor
void UpdateBlocked( void ); ///< Updates the (un)blocked status of the nav area
void CheckFloor( CBaseEntity *ignore ); ///< Checks if there is a floor under the nav area, in case a breakable floor is gone
bool IsBlocked( void ) const { return m_isBlocked; }
void CheckWaterLevel( void );
bool IsUnderwater( void ) const { return m_isUnderwater; }
bool IsOverlapping( const Vector &pos, float tolerance = 0.0f ) 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
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
bool IsCoplanar( const CNavArea *area ) const; ///< return true if this area and given area are approximately co-planar
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( void ) const; ///< return true if this area is badly formed
bool IsRoughlySquare( void ) const; ///< return true if this area is approximately square
bool IsFlat( void ) const; ///< return true if this area is approximately flat
bool IsEdge( NavDirType dir ) const; ///< return true if there are no bi-directional links on the given side
bool IsVisible( const Vector &eye, Vector *visSpot = NULL ) const; ///< return true if area is visible from the given eyepoint, return visible spot
int GetAdjacentCount( NavDirType dir ) const { return m_connect[ dir ].Count(); } ///< return number of connected areas in given direction
CNavArea *GetAdjacentArea( NavDirType dir, int i ) const; /// return the i'th adjacent area in the given direction
CNavArea *GetRandomAdjacentArea( NavDirType dir ) const;
const NavConnectList *GetAdjacentList( NavDirType dir ) const { return &m_connect[dir]; }
bool IsConnected( const CNavArea *area, NavDirType dir ) const; ///< return true if given area is connected in given direction
bool IsConnected( const CNavLadder *ladder, CNavLadder::LadderDirectionType dir ) const; ///< return true if given ladder is connected in given direction
float ComputeHeightChange( const CNavArea *area ); ///< compute change in height from this area to given area
const NavLadderConnectList *GetLadderList( CNavLadder::LadderDirectionType dir ) const { return &m_ladder[dir]; }
void ComputePortal( const CNavArea *to, NavDirType dir, Vector *center, float *halfWidth ) const; ///< compute portal to adjacent area
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
//- for hunting algorithm ---------------------------------------------------------------------------
void SetClearedTimestamp( int teamID ); ///< set this area's "clear" timestamp to now
float GetClearedTimestamp( int teamID ) const; ///< get time this area was marked "clear"
//- hiding spots ------------------------------------------------------------------------------------
const HidingSpotList *GetHidingSpotList( void ) const { return &m_hidingSpotList; }
void ComputeHidingSpots( void ); ///< analyze local area neighborhood to find "hiding spots" in this area - for map learning
void ComputeSniperSpots( void ); ///< analyze local area neighborhood to find "sniper spots" in this area - for map learning
SpotEncounter *GetSpotEncounter( const CNavArea *from, const CNavArea *to ); ///< given the areas we are moving between, return the spots we will encounter
void ComputeSpotEncounters( void ); ///< compute spot encounter data - for map learning
int GetSpotEncounterCount( void ) const { return m_spotEncounterList.Count(); }
//- "danger" ----------------------------------------------------------------------------------------
void IncreaseDanger( int teamID, float amount ); ///< increase the danger of this area for the given team
float GetDanger( int teamID ); ///< return the danger of this area (decays over time)
//- extents -----------------------------------------------------------------------------------------
float GetSizeX( void ) const { return m_extent.hi.x - m_extent.lo.x; }
float GetSizeY( void ) const { return m_extent.hi.y - m_extent.lo.y; }
const Extent &GetExtent( void ) const { return m_extent; }
const Vector &GetCenter( void ) const { return m_center; }
const Vector &GetCorner( NavCornerType corner ) const;
void SetCorner( NavCornerType corner, const Vector& newPosition );
void ComputeNormal( Vector *normal, bool alternate = false ) const; ///< Computes the area's normal based on m_extent.lo. If 'alternate' is specified, m_extent.hi is used instead.
//- approach areas ----------------------------------------------------------------------------------
struct ApproachInfo
{
NavConnect here; ///< the approach area
NavConnect prev; ///< the area just before the approach area on the path
NavTraverseType prevToHereHow;
NavConnect next; ///< the area just after the approach area on the path
NavTraverseType hereToNextHow;
};
const ApproachInfo *GetApproachInfo( int i ) const { return &m_approach[i]; }
int GetApproachInfoCount( void ) const { return m_approachCount; }
void ComputeApproachAreas( void ); ///< determine the set of "approach areas" - for map learning
//- occupy time ------------------------------------------------------------------------------------
float GetEarliestOccupyTime( int teamID ) const; ///< returns the minimum time for someone of the given team to reach this spot from their spawn
bool IsBattlefront( void ) const { return m_isBattlefront; } ///< true if this area is a "battlefront" - where rushing teams initially meet
//- player counting --------------------------------------------------------------------------------
void IncrementPlayerCount( int teamID ); ///< add one player to this area's count
void DecrementPlayerCount( int teamID ); ///< subtract one player from this area's count
void ClearPlayerCount( void ); ///< set the player count to zero
unsigned char GetPlayerCount( int teamID = 0 ) const; ///< return number of players of given team currently within this area (team of zero means any/all)
//- A* pathfinding algorithm ------------------------------------------------------------------------
static void MakeNewMarker( void ) { ++m_masterMarker; if (m_masterMarker == 0) m_masterMarker = 1; }
void Mark( void ) { m_marker = m_masterMarker; }
BOOL IsMarked( void ) const { return (m_marker == m_masterMarker) ? true : false; }
void SetParent( CNavArea *parent, NavTraverseType how = NUM_TRAVERSE_TYPES ) { m_parent = parent; m_parentHow = how; }
CNavArea *GetParent( void ) const { return m_parent; }
NavTraverseType GetParentHow( void ) const { return m_parentHow; }
bool IsOpen( void ) const; ///< true if on "open list"
void AddToOpenList( void ); ///< add to open list in decreasing value order
void UpdateOnOpenList( void ); ///< a smaller value has been found, update this area on the open list
void RemoveFromOpenList( void );
static bool IsOpenListEmpty( void );
static CNavArea *PopOpenList( void ); ///< remove and return the first element of the open list
bool IsClosed( void ) const; ///< true if on "closed list"
void AddToClosedList( void ); ///< add to the closed list
void RemoveFromClosedList( void );
static void ClearSearchLists( void ); ///< clears the open and closed lists for a new search
void SetTotalCost( float value ) { m_totalCost = value; }
float GetTotalCost( void ) const { return m_totalCost; }
void SetCostSoFar( float value ) { m_costSoFar = value; }
float GetCostSoFar( void ) const { return m_costSoFar; }
//- editing -----------------------------------------------------------------------------------------
void Draw( void ) const; ///< draw area for debugging & editing
void DrawConnectedAreas( void ) const;
void DrawHidingSpots( void ) const;
bool SplitEdit( bool splitAlongX, float splitEdge, CNavArea **outAlpha = NULL, CNavArea **outBeta = NULL ); ///< split this area into two areas at the given edge
bool MergeEdit( CNavArea *adj ); ///< merge this area and given adjacent area
bool SpliceEdit( CNavArea *other ); ///< create a new area between this area and given area
void RaiseCorner( NavCornerType corner, int amount, bool raiseAdjacentCorners = true ); ///< raise/lower a corner (or all corners if corner == NUM_CORNERS)
void PlaceOnGround( NavCornerType corner, float inset = 0.0f ); ///< places a corner (or all corners if corner == NUM_CORNERS) on the ground
NavCornerType GetCornerUnderCursor( void ) const;
bool GetCornerHotspot( NavCornerType corner, Vector hotspot[NUM_CORNERS] ) const; ///< returns true if the corner is under the cursor
//- ladders -----------------------------------------------------------------------------------------
void AddLadderUp( CNavLadder *ladder );
void AddLadderDown( CNavLadder *ladder );
private:
friend class CNavMesh;
friend class CNavLadder;
void Initialize( void ); ///< to keep constructors consistent
static bool m_isReset; ///< if true, don't bother cleaning up in destructor since everything is going away
/*
extent.lo
nw ne
+-----------+
| +-->x |
| | |
| v |
| y |
| |
+-----------+
sw se
extent.hi
*/
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 short m_attributeFlags; ///< set of attribute bit flags (see NavAttributeType)
Place m_place; ///< place descriptor
bool m_isBlocked; ///< if true, some part of the world is preventing movement through this nav area
bool m_isUnderwater; ///< true if the center of the area is underwater
/// height of the implicit corners
float m_neZ;
float m_swZ;
//- for hunting -------------------------------------------------------------------------------------
float m_clearedTimestamp[ MAX_NAV_TEAMS ]; ///< time this area was last "cleared" of enemies
//- "danger" ----------------------------------------------------------------------------------------
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
float m_dangerTimestamp[ MAX_NAV_TEAMS ]; ///< time when danger value was set - used for decaying
void DecayDanger( void );
//- hiding spots ------------------------------------------------------------------------------------
HidingSpotList m_hidingSpotList;
bool IsHidingSpotCollision( const Vector &pos ) const; ///< returns true if an existing hiding spot is too close to given position
//- 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 ); ///< add spot encounter data when moving from area to area
//- approach areas ----------------------------------------------------------------------------------
enum { MAX_APPROACH_AREAS = 16 };
ApproachInfo m_approach[ MAX_APPROACH_AREAS ];
unsigned char m_approachCount;
float m_earliestOccupyTime[ MAX_NAV_TEAMS ]; ///< min time to reach this spot from spawn
void ComputeEarliestOccupyTimes( void );
bool m_isBattlefront;
unsigned char m_playerCount[ MAX_NAV_TEAMS ]; ///< the number of players currently in this area
void Strip( void ); ///< remove "analyzed" data from nav area
//- A* pathfinding algorithm ------------------------------------------------------------------------
static unsigned int m_masterMarker;
unsigned int m_marker; ///< used to flag the area as visited
CNavArea *m_parent; ///< the area just prior to this on in the search path
NavTraverseType m_parentHow; ///< how we get from parent to us
float m_totalCost; ///< the distance so far plus an estimate of the distance left
float m_costSoFar; ///< distance travelled so far
static CNavArea *m_openList;
CNavArea *m_nextOpen, *m_prevOpen; ///< only valid if m_openMarker == m_masterMarker
unsigned int m_openMarker; ///< if this equals the current marker value, we are on the open list
//- connections to adjacent areas -------------------------------------------------------------------
NavConnectList m_connect[ NUM_DIRECTIONS ]; ///< a list of adjacent areas for each direction
NavLadderConnectList m_ladder[ CNavLadder::NUM_LADDER_DIRECTIONS ]; ///< list of ladders leading up and down from this area
//---------------------------------------------------------------------------------------------------
CNavNode *m_node[ NUM_CORNERS ]; ///< nav nodes at each corner of the area
void ResetNodes( void ); ///< nodes are going away as part of an incremental nav generation
bool HasNodes( void ) const;
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
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
CUtlLinkedList<CNavArea *, int> m_overlapList; ///< list of areas that overlap this area
void OnDestroyNotify( CNavArea *dead ); ///< invoked when given area is going away
void OnDestroyNotify( CNavLadder *dead ); ///< invoked when given ladder is going away
CNavArea *m_prevHash, *m_nextHash; ///< for hash table in CNavMesh
};
typedef CUtlLinkedList< CNavArea *, int > NavAreaList;
extern NavAreaList TheNavAreaList;
//--------------------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------------------
//
// Inlines
//
//--------------------------------------------------------------------------------------------------------------
inline bool CNavArea::IsDegenerate( void ) const
{
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
{
int iter;
for( iter = m_connect[dir].Head(); iter != m_connect[dir].InvalidIndex(); iter=m_connect[dir].Next( iter ) )
{
if (i == 0)
return m_connect[dir][iter].area;
--i;
}
return NULL;
}
//--------------------------------------------------------------------------------------------------------------
inline bool CNavArea::IsOpen( void ) const
{
return (m_openMarker == m_masterMarker) ? true : false;
}
//--------------------------------------------------------------------------------------------------------------
inline bool CNavArea::IsOpenListEmpty( void )
{
return (m_openList) ? false : true;
}
//--------------------------------------------------------------------------------------------------------------
inline CNavArea *CNavArea::PopOpenList( void )
{
if (m_openList)
{
CNavArea *area = m_openList;
// disconnect from list
area->RemoveFromOpenList();
return area;
}
return NULL;
}
//--------------------------------------------------------------------------------------------------------------
inline bool CNavArea::IsClosed( void ) const
{
if (IsMarked() && !IsOpen())
return true;
return false;
}
//--------------------------------------------------------------------------------------------------------------
inline void CNavArea::AddToClosedList( void )
{
Mark();
}
//--------------------------------------------------------------------------------------------------------------
inline void CNavArea::RemoveFromClosedList( void )
{
// since "closed" is defined as visited (marked) and not on open list, do nothing
}
//--------------------------------------------------------------------------------------------------------------
inline void CNavArea::SetClearedTimestamp( int teamID )
{
m_clearedTimestamp[ teamID % MAX_NAV_TEAMS ] = gpGlobals->curtime;
}
//--------------------------------------------------------------------------------------------------------------
inline float CNavArea::GetClearedTimestamp( int teamID ) const
{
return m_clearedTimestamp[ teamID % MAX_NAV_TEAMS ];
}
//--------------------------------------------------------------------------------------------------------------
inline float CNavArea::GetEarliestOccupyTime( int teamID ) const
{
return m_earliestOccupyTime[ teamID % MAX_NAV_TEAMS ];
}
//--------------------------------------------------------------------------------------------------------------
inline bool CNavArea::IsVisible( const Vector &eye, Vector *visSpot ) const
{
Vector corner;
trace_t result;
CTraceFilterNoNPCsOrPlayer traceFilter( NULL, COLLISION_GROUP_NONE );
const float offset = 0.75f * HumanHeight;
// check center first
UTIL_TraceLine( eye, GetCenter() + Vector( 0, 0, offset ), MASK_OPAQUE | CONTENTS_MONSTER, &traceFilter, &result );
if (result.fraction == 1.0f)
{
// we can see this area
if (visSpot)
{
*visSpot = GetCenter();
}
return true;
}
for( int c=0; c<NUM_CORNERS; ++c )
{
corner = GetCorner( (NavCornerType)c );
UTIL_TraceLine( eye, corner + Vector( 0, 0, offset ), MASK_OPAQUE | CONTENTS_MONSTER, &traceFilter, &result );
if (result.fraction == 1.0f)
{
// we can see this area
if (visSpot)
{
*visSpot = corner;
}
return true;
}
}
return false;
}
inline void CNavArea::IncrementPlayerCount( int teamID )
{
++m_playerCount[ teamID % MAX_NAV_TEAMS ];
}
inline void CNavArea::DecrementPlayerCount( int teamID )
{
--m_playerCount[ teamID % MAX_NAV_TEAMS ];
}
inline void CNavArea::ClearPlayerCount( void )
{
for( int i=0; i<MAX_NAV_TEAMS; ++i )
{
m_playerCount[ i ] = 0;
}
}
inline unsigned char CNavArea::GetPlayerCount( int teamID ) const
{
if (teamID)
{
return m_playerCount[ teamID % MAX_NAV_TEAMS ];
}
// sum all players
unsigned char total = 0;
for( int i=0; i<MAX_NAV_TEAMS; ++i )
{
total += m_playerCount[i];
}
return total;
}
#endif // _NAV_AREA_H_