2020-04-22 16:56:21 +00:00
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//
//=============================================================================//
// nav_file.cpp
// Reading and writing nav files
// Author: Michael S. Booth (mike@turtlerockstudios.com), January-September 2003
# include "cbase.h"
# include "nav_mesh.h"
# include "gamerules.h"
# include "datacache/imdlcache.h"
# ifdef TERROR
# include "func_elevator.h"
# endif
# include "tier1/lzmaDecoder.h"
# ifdef CSTRIKE_DLL
# include "cs_shareddefs.h"
# include "nav_pathfind.h"
# include "cs_nav_area.h"
# endif
// NOTE: This has to be the last file included!
# include "tier0/memdbgon.h"
//--------------------------------------------------------------------------------------------------------------
/// The current version of the nav file format
/// IMPORTANT: If this version changes, the swap function in makegamedata
/// must be updated to match. If not, this will break the Xbox 360.
// TODO: Was changed from 15, update when latest 360 code is integrated (MSB 5/5/09)
const int NavCurrentVersion = 16 ;
//--------------------------------------------------------------------------------------------------------------
//
// The 'place directory' is used to save and load places from
// nav files in a size-efficient manner that also allows for the
// order of the place ID's to change without invalidating the
// nav files.
//
// The place directory is stored in the nav file as a list of
// place name strings. Each nav area then contains an index
// into that directory, or zero if no place has been assigned to
// that area.
//
PlaceDirectory : : PlaceDirectory ( void )
{
Reset ( ) ;
}
void PlaceDirectory : : Reset ( void )
{
m_directory . RemoveAll ( ) ;
m_hasUnnamedAreas = false ;
}
/// return true if this place is already in the directory
bool PlaceDirectory : : IsKnown ( Place place ) const
{
return m_directory . HasElement ( place ) ;
}
/// return the directory index corresponding to this Place (0 = no entry)
PlaceDirectory : : IndexType PlaceDirectory : : GetIndex ( Place place ) const
{
if ( place = = UNDEFINED_PLACE )
return 0 ;
int i = m_directory . Find ( place ) ;
if ( i < 0 )
{
AssertMsg ( false , " PlaceDirectory::GetIndex failure " ) ;
return 0 ;
}
return ( IndexType ) ( i + 1 ) ;
}
/// add the place to the directory if not already known
void PlaceDirectory : : AddPlace ( Place place )
{
if ( place = = UNDEFINED_PLACE )
{
m_hasUnnamedAreas = true ;
return ;
}
Assert ( place < 1000 ) ;
if ( IsKnown ( place ) )
return ;
m_directory . AddToTail ( place ) ;
}
/// given an index, return the Place
Place PlaceDirectory : : IndexToPlace ( IndexType entry ) const
{
if ( entry = = 0 )
return UNDEFINED_PLACE ;
int i = entry - 1 ;
if ( i > = m_directory . Count ( ) )
{
AssertMsg ( false , " PlaceDirectory::IndexToPlace: Invalid entry " ) ;
return UNDEFINED_PLACE ;
}
return m_directory [ i ] ;
}
/// store the directory
void PlaceDirectory : : Save ( CUtlBuffer & fileBuffer )
{
// store number of entries in directory
IndexType count = ( IndexType ) m_directory . Count ( ) ;
fileBuffer . PutUnsignedShort ( count ) ;
// store entries
for ( int i = 0 ; i < m_directory . Count ( ) ; + + i )
{
const char * placeName = TheNavMesh - > PlaceToName ( m_directory [ i ] ) ;
// store string length followed by string itself
unsigned short len = ( unsigned short ) ( strlen ( placeName ) + 1 ) ;
fileBuffer . PutUnsignedShort ( len ) ;
fileBuffer . Put ( placeName , len ) ;
}
fileBuffer . PutUnsignedChar ( m_hasUnnamedAreas ) ;
}
/// load the directory
void PlaceDirectory : : Load ( CUtlBuffer & fileBuffer , int version )
{
// read number of entries
IndexType count = fileBuffer . GetUnsignedShort ( ) ;
m_directory . RemoveAll ( ) ;
// read each entry
char placeName [ 256 ] ;
unsigned short len ;
for ( int i = 0 ; i < count ; + + i )
{
len = fileBuffer . GetUnsignedShort ( ) ;
fileBuffer . Get ( placeName , MIN ( sizeof ( placeName ) , len ) ) ;
Place place = TheNavMesh - > NameToPlace ( placeName ) ;
if ( place = = UNDEFINED_PLACE )
{
Warning ( " Warning: NavMesh place %s is undefined? \n " , placeName ) ;
}
AddPlace ( place ) ;
}
if ( version > 11 )
{
m_hasUnnamedAreas = fileBuffer . GetUnsignedChar ( ) ! = 0 ;
}
}
PlaceDirectory placeDirectory ;
# if defined( _X360 )
# define FORMAT_BSPFILE "maps\\%s.360.bsp"
# define FORMAT_NAVFILE "maps\\%s.360.nav"
# else
# define FORMAT_BSPFILE "maps\\%s.bsp"
# define FORMAT_NAVFILE "maps\\%s.nav"
# endif
//--------------------------------------------------------------------------------------------------------------
/**
* Replace extension with " bsp "
*/
char * GetBspFilename ( const char * navFilename )
{
static char bspFilename [ 256 ] ;
Q_snprintf ( bspFilename , sizeof ( bspFilename ) , FORMAT_BSPFILE , STRING ( gpGlobals - > mapname ) ) ;
int len = strlen ( bspFilename ) ;
if ( len < 3 )
return NULL ;
bspFilename [ len - 3 ] = ' b ' ;
bspFilename [ len - 2 ] = ' s ' ;
bspFilename [ len - 1 ] = ' p ' ;
return bspFilename ;
}
//--------------------------------------------------------------------------------------------------------------
/**
* Save a navigation area to the opened binary stream
*/
void CNavArea : : Save ( CUtlBuffer & fileBuffer , unsigned int version ) const
{
// save ID
fileBuffer . PutUnsignedInt ( m_id ) ;
// save attribute flags
fileBuffer . PutInt ( m_attributeFlags ) ;
// save extent of area
fileBuffer . Put ( & m_nwCorner , 3 * sizeof ( float ) ) ;
fileBuffer . Put ( & m_seCorner , 3 * sizeof ( float ) ) ;
// save heights of implicit corners
fileBuffer . PutFloat ( m_neZ ) ;
fileBuffer . PutFloat ( m_swZ ) ;
// save connections to adjacent areas
// in the enum order NORTH, EAST, SOUTH, WEST
for ( int d = 0 ; d < NUM_DIRECTIONS ; d + + )
{
// save number of connections for this direction
unsigned int count = m_connect [ d ] . Count ( ) ;
fileBuffer . PutUnsignedInt ( count ) ;
FOR_EACH_VEC ( m_connect [ d ] , it )
{
NavConnect connect = m_connect [ d ] [ it ] ;
fileBuffer . PutUnsignedInt ( connect . area - > m_id ) ;
}
}
//
// Store hiding spots for this area
//
unsigned char count ;
if ( m_hidingSpots . Count ( ) > 255 )
{
count = 255 ;
Warning ( " Warning: NavArea #%d: Truncated hiding spot list to 255 \n " , m_id ) ;
}
else
{
count = ( unsigned char ) m_hidingSpots . Count ( ) ;
}
fileBuffer . PutUnsignedChar ( count ) ;
// store HidingSpot objects
unsigned int saveCount = 0 ;
FOR_EACH_VEC ( m_hidingSpots , hit )
{
HidingSpot * spot = m_hidingSpots [ hit ] ;
spot - > Save ( fileBuffer , version ) ;
// overflow check
if ( + + saveCount = = count )
break ;
}
//
// Save encounter spots for this area
//
{
// save number of encounter paths for this area
unsigned int count = m_spotEncounters . Count ( ) ;
fileBuffer . PutUnsignedInt ( count ) ;
SpotEncounter * e ;
FOR_EACH_VEC ( m_spotEncounters , it )
{
e = m_spotEncounters [ it ] ;
if ( e - > from . area )
fileBuffer . PutUnsignedInt ( e - > from . area - > m_id ) ;
else
fileBuffer . PutUnsignedInt ( 0 ) ;
unsigned char dir = ( unsigned char ) e - > fromDir ;
fileBuffer . PutUnsignedChar ( dir ) ;
if ( e - > to . area )
fileBuffer . PutUnsignedInt ( e - > to . area - > m_id ) ;
else
fileBuffer . PutUnsignedInt ( 0 ) ;
dir = ( unsigned char ) e - > toDir ;
fileBuffer . PutUnsignedChar ( dir ) ;
// write list of spots along this path
unsigned char spotCount ;
if ( e - > spots . Count ( ) > 255 )
{
spotCount = 255 ;
Warning ( " Warning: NavArea #%d: Truncated encounter spot list to 255 \n " , m_id ) ;
}
else
{
spotCount = ( unsigned char ) e - > spots . Count ( ) ;
}
fileBuffer . PutUnsignedChar ( spotCount ) ;
saveCount = 0 ;
FOR_EACH_VEC ( e - > spots , sit )
{
SpotOrder * order = & e - > spots [ sit ] ;
// order->spot may be NULL if we've loaded a nav mesh that has been edited but not re-analyzed
unsigned int id = ( order - > spot ) ? order - > spot - > GetID ( ) : 0 ;
fileBuffer . PutUnsignedInt ( id ) ;
unsigned char t = ( unsigned char ) ( 255 * order - > t ) ;
fileBuffer . PutUnsignedChar ( t ) ;
// overflow check
if ( + + saveCount = = spotCount )
break ;
}
}
}
// store place dictionary entry
PlaceDirectory : : IndexType entry = placeDirectory . GetIndex ( GetPlace ( ) ) ;
fileBuffer . Put ( & entry , sizeof ( entry ) ) ;
// write out ladder info
int i ;
for ( i = 0 ; i < CNavLadder : : NUM_LADDER_DIRECTIONS ; + + i )
{
// save number of encounter paths for this area
unsigned int count = m_ladder [ i ] . Count ( ) ;
fileBuffer . PutUnsignedInt ( count ) ;
NavLadderConnect ladder ;
FOR_EACH_VEC ( m_ladder [ i ] , it )
{
ladder = m_ladder [ i ] [ it ] ;
unsigned int id = ladder . ladder - > GetID ( ) ;
fileBuffer . PutUnsignedInt ( id ) ;
}
}
// save earliest occupy times
for ( i = 0 ; i < MAX_NAV_TEAMS ; + + i )
{
// no spot in the map should take longer than this to reach
fileBuffer . Put ( & m_earliestOccupyTime [ i ] , sizeof ( m_earliestOccupyTime [ i ] ) ) ;
}
// save light intensity
for ( i = 0 ; i < NUM_CORNERS ; + + i )
{
fileBuffer . PutFloat ( m_lightIntensity [ i ] ) ;
}
// save visible area set
unsigned int visibleAreaCount = m_potentiallyVisibleAreas . Count ( ) ;
fileBuffer . PutUnsignedInt ( visibleAreaCount ) ;
for ( int vit = 0 ; vit < m_potentiallyVisibleAreas . Count ( ) ; + + vit )
{
CNavArea * area = m_potentiallyVisibleAreas [ vit ] . area ;
unsigned int id = area ? area - > GetID ( ) : 0 ;
fileBuffer . PutUnsignedInt ( id ) ;
fileBuffer . PutUnsignedChar ( m_potentiallyVisibleAreas [ vit ] . attributes ) ;
}
// store area we inherit visibility from
unsigned int id = ( m_inheritVisibilityFrom . area ) ? m_inheritVisibilityFrom . area - > GetID ( ) : 0 ;
fileBuffer . PutUnsignedInt ( id ) ;
}
//--------------------------------------------------------------------------------------------------------------
/**
* Load a navigation area from the file
*/
NavErrorType CNavArea : : Load ( CUtlBuffer & fileBuffer , unsigned int version , unsigned int subVersion )
{
// load ID
m_id = fileBuffer . GetUnsignedInt ( ) ;
// update nextID to avoid collisions
if ( m_id > = m_nextID )
m_nextID = m_id + 1 ;
// load attribute flags
if ( version < = 8 )
{
m_attributeFlags = fileBuffer . GetUnsignedChar ( ) ;
}
else if ( version < 13 )
{
m_attributeFlags = fileBuffer . GetUnsignedShort ( ) ;
}
else
{
m_attributeFlags = fileBuffer . GetInt ( ) ;
}
// load extent of area
fileBuffer . Get ( & m_nwCorner , 3 * sizeof ( float ) ) ;
fileBuffer . Get ( & m_seCorner , 3 * sizeof ( float ) ) ;
m_center . x = ( m_nwCorner . x + m_seCorner . x ) / 2.0f ;
m_center . y = ( m_nwCorner . y + m_seCorner . y ) / 2.0f ;
m_center . z = ( m_nwCorner . z + m_seCorner . z ) / 2.0f ;
if ( ( m_seCorner . x - m_nwCorner . x ) > 0.0f & & ( m_seCorner . y - m_nwCorner . y ) > 0.0f )
{
m_invDxCorners = 1.0f / ( m_seCorner . x - m_nwCorner . x ) ;
m_invDyCorners = 1.0f / ( m_seCorner . y - m_nwCorner . y ) ;
}
else
{
m_invDxCorners = m_invDyCorners = 0 ;
DevWarning ( " Degenerate Navigation Area #%d at setpos %g %g %g \n " ,
m_id , m_center . x , m_center . y , m_center . z ) ;
}
// load heights of implicit corners
m_neZ = fileBuffer . GetFloat ( ) ;
m_swZ = fileBuffer . GetFloat ( ) ;
CheckWaterLevel ( ) ;
// load connections (IDs) to adjacent areas
// in the enum order NORTH, EAST, SOUTH, WEST
for ( int d = 0 ; d < NUM_DIRECTIONS ; d + + )
{
// load number of connections for this direction
unsigned int count = fileBuffer . GetUnsignedInt ( ) ;
Assert ( fileBuffer . IsValid ( ) ) ;
m_connect [ d ] . EnsureCapacity ( count ) ;
for ( unsigned int i = 0 ; i < count ; + + i )
{
NavConnect connect ;
connect . id = fileBuffer . GetUnsignedInt ( ) ;
Assert ( fileBuffer . IsValid ( ) ) ;
// don't allow self-referential connections
if ( connect . id ! = m_id )
{
m_connect [ d ] . AddToTail ( connect ) ;
}
}
}
//
// Load hiding spots
//
// load number of hiding spots
unsigned char hidingSpotCount = fileBuffer . GetUnsignedChar ( ) ;
if ( version = = 1 )
{
// load simple vector array
Vector pos ;
for ( int h = 0 ; h < hidingSpotCount ; + + h )
{
fileBuffer . Get ( & pos , 3 * sizeof ( float ) ) ;
// create new hiding spot and put on master list
HidingSpot * spot = TheNavMesh - > CreateHidingSpot ( ) ;
spot - > SetPosition ( pos ) ;
spot - > SetFlags ( HidingSpot : : IN_COVER ) ;
m_hidingSpots . AddToTail ( spot ) ;
}
}
else
{
// load HidingSpot objects for this area
for ( int h = 0 ; h < hidingSpotCount ; + + h )
{
// create new hiding spot and put on master list
HidingSpot * spot = TheNavMesh - > CreateHidingSpot ( ) ;
spot - > Load ( fileBuffer , version ) ;
m_hidingSpots . AddToTail ( spot ) ;
}
}
if ( version < 15 )
{
//
// Eat the approach areas
//
int nToEat = fileBuffer . GetUnsignedChar ( ) ;
// load approach area info (IDs)
for ( int a = 0 ; a < nToEat ; + + a )
{
fileBuffer . GetUnsignedInt ( ) ;
fileBuffer . GetUnsignedInt ( ) ;
fileBuffer . GetUnsignedChar ( ) ;
fileBuffer . GetUnsignedInt ( ) ;
fileBuffer . GetUnsignedChar ( ) ;
}
}
//
// Load encounter paths for this area
//
unsigned int count = fileBuffer . GetUnsignedInt ( ) ;
if ( version < 3 )
{
// old data, read and discard
for ( unsigned int e = 0 ; e < count ; + + e )
{
SpotEncounter encounter ;
encounter . from . id = fileBuffer . GetUnsignedInt ( ) ;
encounter . to . id = fileBuffer . GetUnsignedInt ( ) ;
fileBuffer . Get ( & encounter . path . from . x , 3 * sizeof ( float ) ) ;
fileBuffer . Get ( & encounter . path . to . x , 3 * sizeof ( float ) ) ;
// read list of spots along this path
unsigned char spotCount = fileBuffer . GetUnsignedChar ( ) ;
for ( int s = 0 ; s < spotCount ; + + s )
{
fileBuffer . GetFloat ( ) ;
fileBuffer . GetFloat ( ) ;
fileBuffer . GetFloat ( ) ;
fileBuffer . GetFloat ( ) ;
}
}
return NAV_OK ;
}
for ( unsigned int e = 0 ; e < count ; + + e )
{
SpotEncounter * encounter = new SpotEncounter ;
encounter - > from . id = fileBuffer . GetUnsignedInt ( ) ;
unsigned char dir = fileBuffer . GetUnsignedChar ( ) ;
encounter - > fromDir = static_cast < NavDirType > ( dir ) ;
encounter - > to . id = fileBuffer . GetUnsignedInt ( ) ;
dir = fileBuffer . GetUnsignedChar ( ) ;
encounter - > toDir = static_cast < NavDirType > ( dir ) ;
// read list of spots along this path
unsigned char spotCount = fileBuffer . GetUnsignedChar ( ) ;
SpotOrder order ;
for ( int s = 0 ; s < spotCount ; + + s )
{
order . id = fileBuffer . GetUnsignedInt ( ) ;
unsigned char t = fileBuffer . GetUnsignedChar ( ) ;
order . t = ( float ) t / 255.0f ;
encounter - > spots . AddToTail ( order ) ;
}
m_spotEncounters . AddToTail ( encounter ) ;
}
if ( version < 5 )
return NAV_OK ;
//
// Load Place data
//
PlaceDirectory : : IndexType entry = fileBuffer . GetUnsignedShort ( ) ;
// convert entry to actual Place
SetPlace ( placeDirectory . IndexToPlace ( entry ) ) ;
if ( version < 7 )
return NAV_OK ;
// load ladder data
for ( int dir = 0 ; dir < CNavLadder : : NUM_LADDER_DIRECTIONS ; + + dir )
{
count = fileBuffer . GetUnsignedInt ( ) ;
for ( unsigned int i = 0 ; i < count ; + + i )
{
NavLadderConnect connect ;
connect . id = fileBuffer . GetUnsignedInt ( ) ;
bool alreadyConnected = false ;
FOR_EACH_VEC ( m_ladder [ dir ] , j )
{
if ( m_ladder [ dir ] [ j ] . id = = connect . id )
{
alreadyConnected = true ;
break ;
}
}
if ( ! alreadyConnected )
{
m_ladder [ dir ] . AddToTail ( connect ) ;
}
}
}
if ( version < 8 )
return NAV_OK ;
// load earliest occupy times
for ( int i = 0 ; i < MAX_NAV_TEAMS ; + + i )
{
// no spot in the map should take longer than this to reach
m_earliestOccupyTime [ i ] = fileBuffer . GetFloat ( ) ;
}
if ( version < 11 )
return NAV_OK ;
// load light intensity
for ( int i = 0 ; i < NUM_CORNERS ; + + i )
{
m_lightIntensity [ i ] = fileBuffer . GetFloat ( ) ;
}
if ( version < 16 )
return NAV_OK ;
// load visibility information
unsigned int visibleAreaCount = fileBuffer . GetUnsignedInt ( ) ;
if ( ! IsX360 ( ) )
{
m_potentiallyVisibleAreas . EnsureCapacity ( visibleAreaCount ) ;
}
else
{
/* TODO: Re-enable when latest 360 code gets integrated (MSB 5/5/09)
size_t nBytes = visibleAreaCount * sizeof ( AreaBindInfo ) ;
m_potentiallyVisibleAreas . ~ CAreaBindInfoArray ( ) ;
new ( & m_potentiallyVisibleAreas ) CAreaBindInfoArray ( ( AreaBindInfo * ) engine - > AllocLevelStaticData ( nBytes ) , visibleAreaCount ) ;
*/
}
for ( unsigned int j = 0 ; j < visibleAreaCount ; + + j )
{
AreaBindInfo info ;
info . id = fileBuffer . GetUnsignedInt ( ) ;
info . attributes = fileBuffer . GetUnsignedChar ( ) ;
m_potentiallyVisibleAreas . AddToTail ( info ) ;
}
// read area from which we inherit visibility
m_inheritVisibilityFrom . id = fileBuffer . GetUnsignedInt ( ) ;
return NAV_OK ;
}
//--------------------------------------------------------------------------------------------------------------
/**
* Convert loaded IDs to pointers
* Make sure all IDs are converted , even if corrupt data is encountered .
*/
NavErrorType CNavArea : : PostLoad ( void )
{
NavErrorType error = NAV_OK ;
for ( int dir = 0 ; dir < CNavLadder : : NUM_LADDER_DIRECTIONS ; + + dir )
{
FOR_EACH_VEC ( m_ladder [ dir ] , it )
{
NavLadderConnect & connect = m_ladder [ dir ] [ it ] ;
unsigned int id = connect . id ;
if ( TheNavMesh - > GetLadders ( ) . Find ( connect . ladder ) = = TheNavMesh - > GetLadders ( ) . InvalidIndex ( ) )
{
connect . ladder = TheNavMesh - > GetLadderByID ( id ) ;
}
if ( id & & connect . ladder = = NULL )
{
Msg ( " CNavArea::PostLoad: Corrupt navigation ladder data. Cannot connect Navigation Areas. \n " ) ;
error = NAV_CORRUPT_DATA ;
}
}
}
// connect areas together
for ( int d = 0 ; d < NUM_DIRECTIONS ; d + + )
{
FOR_EACH_VEC ( m_connect [ d ] , it )
{
NavConnect * connect = & m_connect [ d ] [ it ] ;
// convert connect ID into an actual area
unsigned int id = connect - > id ;
connect - > area = TheNavMesh - > GetNavAreaByID ( id ) ;
if ( id & & connect - > area = = NULL )
{
Msg ( " CNavArea::PostLoad: Corrupt navigation data. Cannot connect Navigation Areas. \n " ) ;
error = NAV_CORRUPT_DATA ;
}
connect - > length = ( connect - > area - > GetCenter ( ) - GetCenter ( ) ) . Length ( ) ;
}
}
// resolve spot encounter IDs
SpotEncounter * e ;
FOR_EACH_VEC ( m_spotEncounters , it )
{
e = m_spotEncounters [ it ] ;
e - > from . area = TheNavMesh - > GetNavAreaByID ( e - > from . id ) ;
if ( e - > from . area = = NULL )
{
Msg ( " CNavArea::PostLoad: Corrupt navigation data. Missing \" from \" Navigation Area for Encounter Spot. \n " ) ;
error = NAV_CORRUPT_DATA ;
}
e - > to . area = TheNavMesh - > GetNavAreaByID ( e - > to . id ) ;
if ( e - > to . area = = NULL )
{
Msg ( " CNavArea::PostLoad: Corrupt navigation data. Missing \" to \" Navigation Area for Encounter Spot. \n " ) ;
error = NAV_CORRUPT_DATA ;
}
if ( e - > from . area & & e - > to . area )
{
// compute path
float halfWidth ;
ComputePortal ( e - > to . area , e - > toDir , & e - > path . to , & halfWidth ) ;
ComputePortal ( e - > from . area , e - > fromDir , & e - > path . from , & halfWidth ) ;
const float eyeHeight = HalfHumanHeight ;
e - > path . from . z = e - > from . area - > GetZ ( e - > path . from ) + eyeHeight ;
e - > path . to . z = e - > to . area - > GetZ ( e - > path . to ) + eyeHeight ;
}
// resolve HidingSpot IDs
FOR_EACH_VEC ( e - > spots , sit )
{
SpotOrder * order = & e - > spots [ sit ] ;
order - > spot = GetHidingSpotByID ( order - > id ) ;
if ( order - > spot = = NULL )
{
Msg ( " CNavArea::PostLoad: Corrupt navigation data. Missing Hiding Spot \n " ) ;
error = NAV_CORRUPT_DATA ;
}
}
}
// convert visible ID's to pointers to actual areas
for ( int it = 0 ; it < m_potentiallyVisibleAreas . Count ( ) ; + + it )
{
AreaBindInfo & info = m_potentiallyVisibleAreas [ it ] ;
info . area = TheNavMesh - > GetNavAreaByID ( info . id ) ;
if ( info . area = = NULL )
{
Warning ( " Invalid area in visible set for area #%d \n " , GetID ( ) ) ;
}
}
m_inheritVisibilityFrom . area = TheNavMesh - > GetNavAreaByID ( m_inheritVisibilityFrom . id ) ;
Assert ( m_inheritVisibilityFrom . area ! = this ) ;
// remove any invalid areas from the list
AreaBindInfo bad ;
bad . area = NULL ;
while ( m_potentiallyVisibleAreas . FindAndRemove ( bad ) ) ;
// func avoid/prefer attributes are controlled by func_nav_cost entities
ClearAllNavCostEntities ( ) ;
return error ;
}
//--------------------------------------------------------------------------------------------------------------
/**
* Compute travel distance along shortest path from startPos to goalPos .
* Return - 1 if can ' t reach endPos from goalPos .
*/
template < typename CostFunctor >
float NavAreaTravelDistance ( const Vector & startPos , const Vector & goalPos , CostFunctor & costFunc )
{
CNavArea * startArea = TheNavMesh - > GetNearestNavArea ( startPos ) ;
if ( startArea = = NULL )
{
return - 1.0f ;
}
// compute path between areas using given cost heuristic
CNavArea * goalArea = NULL ;
if ( NavAreaBuildPath ( startArea , NULL , & goalPos , costFunc , & goalArea ) = = false )
{
return - 1.0f ;
}
// compute distance along path
if ( goalArea - > GetParent ( ) = = NULL )
{
// both points are in the same area - return euclidean distance
return ( goalPos - startPos ) . Length ( ) ;
}
else
{
CNavArea * area ;
float distance ;
// goalPos is assumed to be inside goalArea (or very close to it) - skip to next area
area = goalArea - > GetParent ( ) ;
distance = ( goalPos - area - > GetCenter ( ) ) . Length ( ) ;
for ( ; area - > GetParent ( ) ; area = area - > GetParent ( ) )
{
distance + = ( area - > GetCenter ( ) - area - > GetParent ( ) - > GetCenter ( ) ) . Length ( ) ;
}
// add in distance to startPos
distance + = ( startPos - area - > GetCenter ( ) ) . Length ( ) ;
return distance ;
}
}
//--------------------------------------------------------------------------------------------------------------
/**
* Determine the earliest time this hiding spot can be reached by either team
*/
void CNavArea : : ComputeEarliestOccupyTimes ( void )
{
# ifdef CSTRIKE_DLL
/// @todo Derive cstrike-specific navigation classes
for ( int i = 0 ; i < MAX_NAV_TEAMS ; + + i )
{
// no spot in the map should take longer than this to reach
m_earliestOccupyTime [ i ] = 120.0f ;
}
if ( nav_quicksave . GetBool ( ) )
return ;
// maximum player speed in units/second
const float playerSpeed = 240.0f ;
ShortestPathCost cost ;
CBaseEntity * spot ;
// determine the shortest time it will take a Terrorist to reach this area
int team = TEAM_TERRORIST % MAX_NAV_TEAMS ;
for ( spot = gEntList . FindEntityByClassname ( NULL , " info_player_terrorist " ) ;
spot ;
spot = gEntList . FindEntityByClassname ( spot , " info_player_terrorist " ) )
{
float travelDistance = NavAreaTravelDistance ( spot - > GetAbsOrigin ( ) , m_center , cost ) ;
if ( travelDistance < 0.0f )
continue ;
float travelTime = travelDistance / playerSpeed ;
if ( travelTime < m_earliestOccupyTime [ team ] )
{
m_earliestOccupyTime [ team ] = travelTime ;
}
}
// determine the shortest time it will take a CT to reach this area
team = TEAM_CT % MAX_NAV_TEAMS ;
for ( spot = gEntList . FindEntityByClassname ( NULL , " info_player_counterterrorist " ) ;
spot ;
spot = gEntList . FindEntityByClassname ( spot , " info_player_counterterrorist " ) )
{
float travelDistance = NavAreaTravelDistance ( spot - > GetAbsOrigin ( ) , m_center , cost ) ;
if ( travelDistance < 0.0f )
continue ;
float travelTime = travelDistance / playerSpeed ;
if ( travelTime < m_earliestOccupyTime [ team ] )
{
m_earliestOccupyTime [ team ] = travelTime ;
}
}
# else
for ( int i = 0 ; i < MAX_NAV_TEAMS ; + + i )
{
m_earliestOccupyTime [ i ] = 0.0f ;
}
# endif
}
//--------------------------------------------------------------------------------------------------------------
/**
* Determine if this area is a " battlefront " area - where two rushing teams first meet .
*/
void CNavMesh : : ComputeBattlefrontAreas ( void )
{
#if 0
# ifdef CSTRIKE_DLL
ShortestPathCost cost ;
CBaseEntity * tSpawn , * ctSpawn ;
for ( tSpawn = gEntList . FindEntityByClassname ( NULL , " info_player_terrorist " ) ;
tSpawn ;
tSpawn = gEntList . FindEntityByClassname ( tSpawn , " info_player_terrorist " ) )
{
CNavArea * tArea = TheNavMesh - > GetNavArea ( tSpawn - > GetAbsOrigin ( ) ) ;
if ( tArea = = NULL )
continue ;
for ( ctSpawn = gEntList . FindEntityByClassname ( NULL , " info_player_counterterrorist " ) ;
ctSpawn ;
ctSpawn = gEntList . FindEntityByClassname ( ctSpawn , " info_player_counterterrorist " ) )
{
CNavArea * ctArea = TheNavMesh - > GetNavArea ( ctSpawn - > GetAbsOrigin ( ) ) ;
if ( ctArea = = NULL )
continue ;
if ( tArea = = ctArea )
{
m_isBattlefront = true ;
return ;
}
// build path between these two spawn points - assume if path fails, it at least got close
// (ie: imagine spawn points that you jump down from - can't path to)
CNavArea * goalArea = NULL ;
NavAreaBuildPath ( tArea , ctArea , NULL , cost , & goalArea ) ;
if ( goalArea = = NULL )
continue ;
/**
* @ todo Need to enumerate ALL paths between all pairs of spawn points to find all battlefront areas
*/
// find the area with the earliest overlapping occupy times
CNavArea * battlefront = NULL ;
float earliestTime = 999999.9f ;
const float epsilon = 1.0f ;
CNavArea * area ;
for ( area = goalArea ; area ; area = area - > GetParent ( ) )
{
if ( fabs ( area - > GetEarliestOccupyTime ( TEAM_TERRORIST ) - area - > GetEarliestOccupyTime ( TEAM_CT ) ) < epsilon )
{
}
}
}
}
# endif
# endif
}
//--------------------------------------------------------------------------------------------------------------
/**
* Return the filename for this map ' s " nav map " file
*/
const char * CNavMesh : : GetFilename ( void ) const
{
// filename is local to game dir for Steam, so we need to prepend game dir for regular file save
char gamePath [ 256 ] ;
engine - > GetGameDir ( gamePath , 256 ) ;
// persistant return value
static char filename [ 256 ] ;
Q_snprintf ( filename , sizeof ( filename ) , " %s \\ " FORMAT_NAVFILE , gamePath , STRING ( gpGlobals - > mapname ) ) ;
return filename ;
}
//--------------------------------------------------------------------------------------------------------------
/*
= = = = = = = = = = = =
COM_FixSlashes
Changes all ' / ' characters into ' \ ' characters , in place .
= = = = = = = = = = = =
*/
inline void COM_FixSlashes ( char * pname )
{
# ifdef _WIN32
while ( * pname )
{
if ( * pname = = ' / ' )
* pname = ' \\ ' ;
pname + + ;
}
# else
while ( * pname )
{
if ( * pname = = ' \\ ' )
* pname = ' / ' ;
pname + + ;
}
# endif
}
static void WarnIfMeshNeedsAnalysis ( int version )
{
// Quick check to warn about needing to analyze: nav_strip, nav_delete, etc set
// every CNavArea's m_approachCount to 0, and delete their m_spotEncounterList.
// So, if no area has either, odds are good we need an analyze.
if ( version > = 14 )
{
if ( ! TheNavMesh - > IsAnalyzed ( ) )
{
Warning ( " The nav mesh needs a full nav_analyze \n " ) ;
return ;
}
}
# ifdef CSTRIKE_DLL
else
{
bool hasApproachAreas = false ;
bool hasSpotEncounters = false ;
FOR_EACH_VEC ( TheNavAreas , it )
{
CCSNavArea * area = dynamic_cast < CCSNavArea * > ( TheNavAreas [ it ] ) ;
if ( area )
{
if ( area - > GetApproachInfoCount ( ) )
{
hasApproachAreas = true ;
}
if ( area - > GetSpotEncounterCount ( ) )
{
hasSpotEncounters = true ;
}
}
}
if ( ! hasApproachAreas | | ! hasSpotEncounters )
{
Warning ( " The nav mesh needs a full nav_analyze \n " ) ;
}
}
# endif
}
/**
* Store Navigation Mesh to a file
*/
bool CNavMesh : : Save ( void ) const
{
WarnIfMeshNeedsAnalysis ( NavCurrentVersion ) ;
const char * filename = GetFilename ( ) ;
if ( filename = = NULL )
return false ;
//
// Store the NAV file
//
COM_FixSlashes ( const_cast < char * > ( filename ) ) ;
// get size of source bsp file for later (before we open the nav file for writing, in
// case of failure)
char * bspFilename = GetBspFilename ( filename ) ;
if ( bspFilename = = NULL )
{
return false ;
}
CUtlBuffer fileBuffer ( 4096 , 1024 * 1024 ) ;
// store "magic number" to help identify this kind of file
unsigned int magic = NAV_MAGIC_NUMBER ;
fileBuffer . PutUnsignedInt ( magic ) ;
// store version number of file
// 1 = hiding spots as plain vector array
// 2 = hiding spots as HidingSpot objects
// 3 = Encounter spots use HidingSpot ID's instead of storing vector again
// 4 = Includes size of source bsp file to verify nav data correlation
// ---- Beta Release at V4 -----
// 5 = Added Place info
// ---- Conversion to Src ------
// 6 = Added Ladder info
// 7 = Areas store ladder ID's so ladders can have one-way connections
// 8 = Added earliest occupy times (2 floats) to each area
// 9 = Promoted CNavArea's attribute flags to a short
// 10 - Added sub-version number to allow derived classes to have custom area data
// 11 - Added light intensity to each area
// 12 - Storing presence of unnamed areas in the PlaceDirectory
// 13 - Widened NavArea attribute bits from unsigned short to int
// 14 - Added a bool for if the nav needs analysis
// 15 - removed approach areas
// 16 - Added visibility data to the base mesh
fileBuffer . PutUnsignedInt ( NavCurrentVersion ) ;
// The sub-version number is maintained and owned by classes derived from CNavMesh and CNavArea
// and allows them to track their custom data just as we do at this top level
fileBuffer . PutUnsignedInt ( GetSubVersionNumber ( ) ) ;
// store the size of source bsp file in the nav file
// so we can test if the bsp changed since the nav file was made
unsigned int bspSize = filesystem - > Size ( bspFilename ) ;
DevMsg ( " Size of bsp file '%s' is %u bytes. \n " , bspFilename , bspSize ) ;
fileBuffer . PutUnsignedInt ( bspSize ) ;
// Store the analysis state
fileBuffer . PutUnsignedChar ( m_isAnalyzed ) ;
//
// Build a directory of the Places in this map
//
placeDirectory . Reset ( ) ;
FOR_EACH_VEC ( TheNavAreas , nit )
{
CNavArea * area = TheNavAreas [ nit ] ;
Place place = area - > GetPlace ( ) ;
placeDirectory . AddPlace ( place ) ;
}
placeDirectory . Save ( fileBuffer ) ;
SaveCustomDataPreArea ( fileBuffer ) ;
//
// Store navigation areas
//
{
// store number of areas
unsigned int count = TheNavAreas . Count ( ) ;
fileBuffer . PutUnsignedInt ( count ) ;
// store each area
FOR_EACH_VEC ( TheNavAreas , it )
{
CNavArea * area = TheNavAreas [ it ] ;
area - > Save ( fileBuffer , NavCurrentVersion ) ;
}
}
//
// Store ladders
//
{
// store number of ladders
unsigned int count = m_ladders . Count ( ) ;
fileBuffer . PutUnsignedInt ( count ) ;
// store each ladder
for ( int i = 0 ; i < m_ladders . Count ( ) ; + + i )
{
CNavLadder * ladder = m_ladders [ i ] ;
ladder - > Save ( fileBuffer , NavCurrentVersion ) ;
}
}
//
// Store derived class mesh info
//
SaveCustomData ( fileBuffer ) ;
if ( ! filesystem - > WriteFile ( filename , " MOD " , fileBuffer ) )
{
Warning ( " Unable to save %d bytes to %s \n " , fileBuffer . Size ( ) , filename ) ;
return false ;
}
unsigned int navSize = filesystem - > Size ( filename ) ;
DevMsg ( " Size of nav file '%s' is %u bytes. \n " , filename , navSize ) ;
return true ;
}
//--------------------------------------------------------------------------------------------------------------
static NavErrorType CheckNavFile ( const char * bspFilename )
{
if ( ! bspFilename )
return NAV_CANT_ACCESS_FILE ;
char baseName [ 256 ] ;
Q_StripExtension ( bspFilename , baseName , sizeof ( baseName ) ) ;
char bspPathname [ 256 ] ;
Q_snprintf ( bspPathname , sizeof ( bspPathname ) , FORMAT_BSPFILE , baseName ) ;
char filename [ 256 ] ;
Q_snprintf ( filename , sizeof ( filename ) , FORMAT_NAVFILE , baseName ) ;
bool navIsInBsp = false ;
FileHandle_t file = filesystem - > Open ( filename , " rb " , " MOD " ) ; // this ignores .nav files embedded in the .bsp ...
if ( ! file )
{
navIsInBsp = true ;
file = filesystem - > Open ( filename , " rb " , " GAME " ) ; // ... and this looks for one if it's the only one around.
}
if ( ! file )
{
return NAV_CANT_ACCESS_FILE ;
}
// check magic number
int result ;
unsigned int magic ;
result = filesystem - > Read ( & magic , sizeof ( unsigned int ) , file ) ;
if ( ! result | | magic ! = NAV_MAGIC_NUMBER )
{
filesystem - > Close ( file ) ;
return NAV_INVALID_FILE ;
}
// read file version number
unsigned int version ;
result = filesystem - > Read ( & version , sizeof ( unsigned int ) , file ) ;
if ( ! result | | version > NavCurrentVersion | | version < 4 )
{
filesystem - > Close ( file ) ;
return NAV_BAD_FILE_VERSION ;
}
// get size of source bsp file and verify that the bsp hasn't changed
unsigned int saveBspSize ;
filesystem - > Read ( & saveBspSize , sizeof ( unsigned int ) , file ) ;
// verify size
unsigned int bspSize = filesystem - > Size ( bspPathname ) ;
if ( bspSize ! = saveBspSize & & ! navIsInBsp )
{
return NAV_FILE_OUT_OF_DATE ;
}
return NAV_OK ;
}
//--------------------------------------------------------------------------------------------------------------
void CommandNavCheckFileConsistency ( void )
{
if ( ! UTIL_IsCommandIssuedByServerAdmin ( ) )
return ;
FileFindHandle_t findHandle ;
const char * bspFilename = filesystem - > FindFirstEx ( " maps/*.bsp " , " MOD " , & findHandle ) ;
while ( bspFilename )
{
switch ( CheckNavFile ( bspFilename ) )
{
case NAV_CANT_ACCESS_FILE :
Warning ( " Missing nav file for %s \n " , bspFilename ) ;
break ;
case NAV_INVALID_FILE :
Warning ( " Invalid nav file for %s \n " , bspFilename ) ;
break ;
case NAV_BAD_FILE_VERSION :
Warning ( " Old nav file for %s \n " , bspFilename ) ;
break ;
case NAV_FILE_OUT_OF_DATE :
Warning ( " The nav file for %s is built from an old version of the map \n " , bspFilename ) ;
break ;
case NAV_OK :
Msg ( " The nav file for %s is up-to-date \n " , bspFilename ) ;
break ;
}
bspFilename = filesystem - > FindNext ( findHandle ) ;
}
filesystem - > FindClose ( findHandle ) ;
}
static ConCommand nav_check_file_consistency ( " nav_check_file_consistency " , CommandNavCheckFileConsistency , " Scans the maps directory and reports any missing/out-of-date navigation files. " , FCVAR_GAMEDLL | FCVAR_CHEAT ) ;
//--------------------------------------------------------------------------------------------------------------
/**
* Reads the used place names from the nav file ( can be used to selectively precache before the nav is loaded )
*/
const CUtlVector < Place > * CNavMesh : : GetPlacesFromNavFile ( bool * hasUnnamedPlaces )
{
placeDirectory . Reset ( ) ;
// nav filename is derived from map filename
char filename [ 256 ] ;
Q_snprintf ( filename , sizeof ( filename ) , FORMAT_NAVFILE , STRING ( gpGlobals - > mapname ) ) ;
CUtlBuffer fileBuffer ( 4096 , 1024 * 1024 , CUtlBuffer : : READ_ONLY ) ;
2022-03-01 20:00:42 +00:00
if ( ! filesystem - > ReadFile ( filename , " GAME " , fileBuffer ) ) // this ignores .nav files embedded in the .bsp ...
2020-04-22 16:56:21 +00:00
{
2022-03-01 20:00:42 +00:00
if ( ! filesystem - > ReadFile ( filename , " BSP " , fileBuffer ) ) // ... and this looks for one if it's the only one around.
{
return NULL ;
}
}
if ( IsX360 ( ) )
{
// 360 has compressed NAVs
CLZMA lzma ;
if ( lzma . IsCompressed ( ( unsigned char * ) fileBuffer . Base ( ) ) )
{
int originalSize = lzma . GetActualSize ( ( unsigned char * ) fileBuffer . Base ( ) ) ;
unsigned char * pOriginalData = new unsigned char [ originalSize ] ;
lzma . Uncompress ( ( unsigned char * ) fileBuffer . Base ( ) , pOriginalData ) ;
fileBuffer . AssumeMemory ( pOriginalData , originalSize , originalSize , CUtlBuffer : : READ_ONLY ) ;
}
2020-04-22 16:56:21 +00:00
}
// check magic number
unsigned int magic = fileBuffer . GetUnsignedInt ( ) ;
if ( ! fileBuffer . IsValid ( ) | | magic ! = NAV_MAGIC_NUMBER )
{
return NULL ; // Corrupt nav file?
}
// read file version number
unsigned int version = fileBuffer . GetUnsignedInt ( ) ;
if ( ! fileBuffer . IsValid ( ) | | version > NavCurrentVersion )
{
return NULL ; // Unknown nav file version
}
if ( version < 5 )
{
return NULL ; // Too old to have place names
}
unsigned int subVersion = 0 ;
if ( version > = 10 )
{
subVersion = fileBuffer . GetUnsignedInt ( ) ;
if ( ! fileBuffer . IsValid ( ) )
{
return NULL ; // No sub-version
}
}
fileBuffer . GetUnsignedInt ( ) ; // skip BSP file size
if ( version > = 14 )
{
fileBuffer . GetUnsignedChar ( ) ; // skip m_isAnalyzed
}
placeDirectory . Load ( fileBuffer , version ) ;
LoadCustomDataPreArea ( fileBuffer , subVersion ) ;
if ( hasUnnamedPlaces )
{
* hasUnnamedPlaces = placeDirectory . HasUnnamedPlaces ( ) ;
}
return placeDirectory . GetPlaces ( ) ;
}
//--------------------------------------------------------------------------------------------------------------
/**
* Load AI navigation data from a file
*/
NavErrorType CNavMesh : : Load ( void )
{
MDLCACHE_CRITICAL_SECTION ( ) ;
// free previous navigation mesh data
Reset ( ) ;
placeDirectory . Reset ( ) ;
CNavVectorNoEditAllocator : : Reset ( ) ;
GameRules ( ) - > OnNavMeshLoad ( ) ;
CNavArea : : m_nextID = 1 ;
2022-03-01 20:00:42 +00:00
// nav filename is derived from map filename
char filename [ 256 ] ;
Q_snprintf ( filename , sizeof ( filename ) , FORMAT_NAVFILE , STRING ( gpGlobals - > mapname ) ) ;
2020-04-22 16:56:21 +00:00
bool navIsInBsp = false ;
CUtlBuffer fileBuffer ( 4096 , 1024 * 1024 , CUtlBuffer : : READ_ONLY ) ;
2022-03-01 20:00:42 +00:00
if ( ! filesystem - > ReadFile ( filename , " MOD " , fileBuffer ) ) // this ignores .nav files embedded in the .bsp ...
{
navIsInBsp = true ;
if ( ! filesystem - > ReadFile ( filename , " BSP " , fileBuffer ) ) // ... and this looks for one if it's the only one around.
{
return NAV_CANT_ACCESS_FILE ;
}
}
if ( IsX360 ( ) )
2020-04-22 16:56:21 +00:00
{
2022-03-01 20:00:42 +00:00
// 360 has compressed NAVs
CLZMA lzma ;
if ( lzma . IsCompressed ( ( unsigned char * ) fileBuffer . Base ( ) ) )
{
int originalSize = lzma . GetActualSize ( ( unsigned char * ) fileBuffer . Base ( ) ) ;
unsigned char * pOriginalData = new unsigned char [ originalSize ] ;
lzma . Uncompress ( ( unsigned char * ) fileBuffer . Base ( ) , pOriginalData ) ;
fileBuffer . AssumeMemory ( pOriginalData , originalSize , originalSize , CUtlBuffer : : READ_ONLY ) ;
}
2020-04-22 16:56:21 +00:00
}
// check magic number
unsigned int magic = fileBuffer . GetUnsignedInt ( ) ;
if ( ! fileBuffer . IsValid ( ) | | magic ! = NAV_MAGIC_NUMBER )
{
2022-03-01 20:00:42 +00:00
Msg ( " Invalid navigation file '%s'. \n " , filename ) ;
2020-04-22 16:56:21 +00:00
return NAV_INVALID_FILE ;
}
// read file version number
unsigned int version = fileBuffer . GetUnsignedInt ( ) ;
if ( ! fileBuffer . IsValid ( ) | | version > NavCurrentVersion )
{
Msg ( " Unknown navigation file version. \n " ) ;
return NAV_BAD_FILE_VERSION ;
}
2022-03-01 20:00:42 +00:00
2020-04-22 16:56:21 +00:00
unsigned int subVersion = 0 ;
if ( version > = 10 )
{
subVersion = fileBuffer . GetUnsignedInt ( ) ;
if ( ! fileBuffer . IsValid ( ) )
{
Msg ( " Error reading sub-version number. \n " ) ;
return NAV_INVALID_FILE ;
}
}
if ( version > = 4 )
{
// get size of source bsp file and verify that the bsp hasn't changed
unsigned int saveBspSize = fileBuffer . GetUnsignedInt ( ) ;
// verify size
2022-03-01 20:00:42 +00:00
char * bspFilename = GetBspFilename ( filename ) ;
if ( bspFilename = = NULL )
{
return NAV_INVALID_FILE ;
}
2020-04-22 16:56:21 +00:00
unsigned int bspSize = filesystem - > Size ( bspFilename ) ;
if ( bspSize ! = saveBspSize & & ! navIsInBsp )
{
if ( engine - > IsDedicatedServer ( ) )
{
// Warning doesn't print to the dedicated server console, so we'll use Msg instead
DevMsg ( " The Navigation Mesh was built using a different version of this map. \n " ) ;
}
else
{
DevWarning ( " The Navigation Mesh was built using a different version of this map. \n " ) ;
}
m_isOutOfDate = true ;
}
}
if ( version > = 14 )
{
m_isAnalyzed = fileBuffer . GetUnsignedChar ( ) ! = 0 ;
}
else
{
m_isAnalyzed = false ;
}
// load Place directory
if ( version > = 5 )
{
placeDirectory . Load ( fileBuffer , version ) ;
}
LoadCustomDataPreArea ( fileBuffer , subVersion ) ;
// get number of areas
unsigned int count = fileBuffer . GetUnsignedInt ( ) ;
unsigned int i ;
if ( count = = 0 )
{
return NAV_INVALID_FILE ;
}
Extent extent ;
extent . lo . x = 9999999999.9f ;
extent . lo . y = 9999999999.9f ;
extent . hi . x = - 9999999999.9f ;
extent . hi . y = - 9999999999.9f ;
// load the areas and compute total extent
TheNavMesh - > PreLoadAreas ( count ) ;
Extent areaExtent ;
for ( i = 0 ; i < count ; + + i )
{
CNavArea * area = TheNavMesh - > CreateArea ( ) ;
area - > Load ( fileBuffer , version , subVersion ) ;
TheNavAreas . AddToTail ( area ) ;
area - > GetExtent ( & areaExtent ) ;
if ( areaExtent . lo . x < extent . lo . x )
extent . lo . x = areaExtent . lo . x ;
if ( areaExtent . lo . y < extent . lo . y )
extent . lo . y = areaExtent . lo . y ;
if ( areaExtent . hi . x > extent . hi . x )
extent . hi . x = areaExtent . hi . x ;
if ( areaExtent . hi . y > extent . hi . y )
extent . hi . y = areaExtent . hi . y ;
}
// add the areas to the grid
AllocateGrid ( extent . lo . x , extent . hi . x , extent . lo . y , extent . hi . y ) ;
FOR_EACH_VEC ( TheNavAreas , it )
{
AddNavArea ( TheNavAreas [ it ] ) ;
}
//
// Set up all the ladders
//
if ( version > = 6 )
{
count = fileBuffer . GetUnsignedInt ( ) ;
m_ladders . EnsureCapacity ( count ) ;
// load the ladders
for ( i = 0 ; i < count ; + + i )
{
CNavLadder * ladder = new CNavLadder ;
ladder - > Load ( fileBuffer , version ) ;
m_ladders . AddToTail ( ladder ) ;
}
}
else
{
BuildLadders ( ) ;
}
// mark stairways (TODO: this can be removed once all maps are re-saved with this attribute in them)
MarkStairAreas ( ) ;
//
// Load derived class mesh info
//
LoadCustomData ( fileBuffer , subVersion ) ;
//
// Bind pointers, etc
//
NavErrorType loadResult = PostLoad ( version ) ;
WarnIfMeshNeedsAnalysis ( version ) ;
return loadResult ;
}
struct OneWayLink_t
{
CNavArea * destArea ;
CNavArea * area ;
int backD ;
static int Compare ( const OneWayLink_t * lhs , const OneWayLink_t * rhs )
{
int result = ( lhs - > destArea - rhs - > destArea ) ;
if ( result ! = 0 )
{
return result ;
}
return ( lhs - > backD - rhs - > backD ) ;
}
} ;
//--------------------------------------------------------------------------------------------------------------
/**
* Invoked after all areas have been loaded - for pointer binding , etc
*/
NavErrorType CNavMesh : : PostLoad ( unsigned int version )
{
// allow areas to connect to each other, etc
FOR_EACH_VEC ( TheNavAreas , pit )
{
CNavArea * area = TheNavAreas [ pit ] ;
area - > PostLoad ( ) ;
}
// allow hiding spots to compute information
FOR_EACH_VEC ( TheHidingSpots , hit )
{
HidingSpot * spot = TheHidingSpots [ hit ] ;
spot - > PostLoad ( ) ;
}
if ( version < 8 )
{
// Old nav meshes need to compute earliest occupy times
FOR_EACH_VEC ( TheNavAreas , nit )
{
CNavArea * area = TheNavAreas [ nit ] ;
area - > ComputeEarliestOccupyTimes ( ) ;
}
}
ComputeBattlefrontAreas ( ) ;
//
// Allow each nav area to know what other areas have one-way connections to it. Need to gather
// then sort due to allocation restrictions on the 360
//
OneWayLink_t oneWayLink ;
CUtlVectorFixedGrowable < OneWayLink_t , 512 > oneWayLinks ;
FOR_EACH_VEC ( TheNavAreas , oit )
{
oneWayLink . area = TheNavAreas [ oit ] ;
for ( int d = 0 ; d < NUM_DIRECTIONS ; d + + )
{
const NavConnectVector * connectList = oneWayLink . area - > GetAdjacentAreas ( ( NavDirType ) d ) ;
FOR_EACH_VEC ( ( * connectList ) , it )
{
NavConnect connect = ( * connectList ) [ it ] ;
oneWayLink . destArea = connect . area ;
// if the area we connect to has no connection back to us, allow that area to remember us as an incoming connection
oneWayLink . backD = OppositeDirection ( ( NavDirType ) d ) ;
const NavConnectVector * backConnectList = oneWayLink . destArea - > GetAdjacentAreas ( ( NavDirType ) oneWayLink . backD ) ;
bool isOneWay = true ;
FOR_EACH_VEC ( ( * backConnectList ) , bit )
{
NavConnect backConnect = ( * backConnectList ) [ bit ] ;
if ( backConnect . area - > GetID ( ) = = oneWayLink . area - > GetID ( ) )
{
isOneWay = false ;
break ;
}
}
if ( isOneWay )
{
oneWayLinks . AddToTail ( oneWayLink ) ;
}
}
}
}
oneWayLinks . Sort ( & OneWayLink_t : : Compare ) ;
for ( int i = 0 ; i < oneWayLinks . Count ( ) ; i + + )
{
// add this one-way connection
oneWayLinks [ i ] . destArea - > AddIncomingConnection ( oneWayLinks [ i ] . area , ( NavDirType ) oneWayLinks [ i ] . backD ) ;
}
ValidateNavAreaConnections ( ) ;
// TERROR: loading into a map directly creates entities before the mesh is loaded. Tell the preexisting
// entities now that the mesh is loaded so they can update areas.
for ( int i = 0 ; i < m_avoidanceObstacles . Count ( ) ; + + i )
{
m_avoidanceObstacles [ i ] - > OnNavMeshLoaded ( ) ;
}
// the Navigation Mesh has been successfully loaded
m_isLoaded = true ;
return NAV_OK ;
}