2008-09-15 01:07:45 -05:00
//========= Copyright <20> 1996-2005, 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"
# ifdef CSTRIKE_DLL
# include "cs_shareddefs.h"
# include "nav_pathfind.h"
# endif
//--------------------------------------------------------------------------------------------------------------
/// The current version of the nav file format
2008-09-15 02:50:57 -05:00
const unsigned int NavCurrentVersion = 9 ;
2008-09-15 01:07:45 -05:00
//--------------------------------------------------------------------------------------------------------------
//
// 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.
//
class PlaceDirectory
{
public :
typedef unsigned short IndexType ;
void Reset ( void )
{
m_directory . RemoveAll ( ) ;
}
/// return true if this place is already in the directory
bool IsKnown ( Place place ) const
{
return m_directory . HasElement ( place ) ;
}
/// return the directory index corresponding to this Place (0 = no entry)
IndexType GetIndex ( Place place ) const
{
if ( place = = UNDEFINED_PLACE )
return 0 ;
int i = m_directory . Find ( place ) ;
if ( i < 0 )
{
Assert ( false & & " PlaceDirectory::GetIndex failure " ) ;
return 0 ;
}
return ( IndexType ) ( i + 1 ) ;
}
/// add the place to the directory if not already known
void AddPlace ( Place place )
{
if ( place = = UNDEFINED_PLACE )
return ;
Assert ( place < 1000 ) ;
if ( IsKnown ( place ) )
return ;
m_directory . AddToTail ( place ) ;
}
/// given an index, return the Place
Place IndexToPlace ( IndexType entry ) const
{
if ( entry = = 0 )
return UNDEFINED_PLACE ;
int i = entry - 1 ;
if ( i > = m_directory . Count ( ) )
{
Assert ( false & & " PlaceDirectory::IndexToPlace: Invalid entry " ) ;
return UNDEFINED_PLACE ;
}
return m_directory [ i ] ;
}
/// store the directory
void Save ( FileHandle_t file )
{
// store number of entries in directory
IndexType count = ( IndexType ) m_directory . Count ( ) ;
filesystem - > Write ( & count , sizeof ( IndexType ) , file ) ;
// 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 ) ;
filesystem - > Write ( & len , sizeof ( unsigned short ) , file ) ;
filesystem - > Write ( placeName , len , file ) ;
}
}
/// load the directory
void Load ( FileHandle_t file )
{
// read number of entries
IndexType count ;
filesystem - > Read ( & count , sizeof ( IndexType ) , file ) ;
m_directory . RemoveAll ( ) ;
// read each entry
char placeName [ 256 ] ;
unsigned short len ;
for ( int i = 0 ; i < count ; + + i )
{
filesystem - > Read ( & len , sizeof ( unsigned short ) , file ) ;
filesystem - > Read ( placeName , len , file ) ;
AddPlace ( TheNavMesh - > NameToPlace ( placeName ) ) ;
}
}
private :
CUtlVector < Place > m_directory ;
} ;
static PlaceDirectory placeDirectory ;
//--------------------------------------------------------------------------------------------------------------
/**
* Replace extension with " bsp "
*/
char * GetBspFilename ( const char * navFilename )
{
static char bspFilename [ 256 ] ;
Q_snprintf ( bspFilename , sizeof ( bspFilename ) , " maps \\ %s.bsp " , 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 ;
}
//--------------------------------------------------------------------------------------------------------------
/*
void CNavArea : : Save ( FILE * fp ) const
{
fprintf ( fp , " v %f %f %f \n " , m_extent . lo . x , m_extent . lo . y , m_extent . lo . z ) ;
fprintf ( fp , " v %f %f %f \n " , m_extent . hi . x , m_extent . lo . y , m_neZ ) ;
fprintf ( fp , " v %f %f %f \n " , m_extent . hi . x , m_extent . hi . y , m_extent . hi . z ) ;
fprintf ( fp , " v %f %f %f \n " , m_extent . lo . x , m_extent . hi . y , m_swZ ) ;
static int base = 1 ;
fprintf ( fp , " \n \n g %04dArea%s%s%s%s \n " , m_id ,
( GetAttributes ( ) & BOT_NAV_CROUCH ) ? " CROUCH " : " " ,
( GetAttributes ( ) & BOT_NAV_JUMP ) ? " JUMP " : " " ,
( GetAttributes ( ) & BOT_NAV_PRECISE ) ? " PRECISE " : " " ,
( GetAttributes ( ) & BOT_NAV_NO_JUMP ) ? " NO_JUMP " : " " ) ;
fprintf ( fp , " f %d %d %d %d \n \n " , base , base + 1 , base + 2 , base + 3 ) ;
base + = 4 ;
}
*/
//--------------------------------------------------------------------------------------------------------------
/**
* Save a navigation area to the opened binary stream
*/
void CNavArea : : Save ( FileHandle_t file , unsigned int version ) const
{
// save ID
filesystem - > Write ( & m_id , sizeof ( unsigned int ) , file ) ;
// save attribute flags
filesystem - > Write ( & m_attributeFlags , sizeof ( unsigned short ) , file ) ;
// save extent of area
filesystem - > Write ( & m_extent , 6 * sizeof ( float ) , file ) ;
// save heights of implicit corners
filesystem - > Write ( & m_neZ , sizeof ( float ) , file ) ;
filesystem - > Write ( & m_swZ , sizeof ( float ) , file ) ;
// 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 ( ) ;
filesystem - > Write ( & count , sizeof ( unsigned int ) , file ) ;
FOR_EACH_LL ( m_connect [ d ] , it )
{
NavConnect connect = m_connect [ d ] [ it ] ;
filesystem - > Write ( & connect . area - > m_id , sizeof ( unsigned int ) , file ) ;
}
}
//
// Store hiding spots for this area
//
unsigned char count ;
if ( m_hidingSpotList . Count ( ) > 255 )
{
count = 255 ;
Warning ( " Warning: NavArea #%d: Truncated hiding spot list to 255 \n " , m_id ) ;
}
else
{
count = ( unsigned char ) m_hidingSpotList . Count ( ) ;
}
filesystem - > Write ( & count , sizeof ( unsigned char ) , file ) ;
// store HidingSpot objects
unsigned int saveCount = 0 ;
FOR_EACH_LL ( m_hidingSpotList , hit )
{
HidingSpot * spot = m_hidingSpotList [ hit ] ;
spot - > Save ( file , version ) ;
// overflow check
if ( + + saveCount = = count )
break ;
}
//
// Save the approach areas for this area
//
// save number of approach areas
filesystem - > Write ( & m_approachCount , sizeof ( unsigned char ) , file ) ;
// save approach area info
unsigned char type ;
unsigned int zero = 0 ;
for ( int a = 0 ; a < m_approachCount ; + + a )
{
if ( m_approach [ a ] . here . area )
filesystem - > Write ( & m_approach [ a ] . here . area - > m_id , sizeof ( unsigned int ) , file ) ;
else
filesystem - > Write ( & zero , sizeof ( unsigned int ) , file ) ;
if ( m_approach [ a ] . prev . area )
filesystem - > Write ( & m_approach [ a ] . prev . area - > m_id , sizeof ( unsigned int ) , file ) ;
else
filesystem - > Write ( & zero , sizeof ( unsigned int ) , file ) ;
type = ( unsigned char ) m_approach [ a ] . prevToHereHow ;
filesystem - > Write ( & type , sizeof ( unsigned char ) , file ) ;
if ( m_approach [ a ] . next . area )
filesystem - > Write ( & m_approach [ a ] . next . area - > m_id , sizeof ( unsigned int ) , file ) ;
else
filesystem - > Write ( & zero , sizeof ( unsigned int ) , file ) ;
type = ( unsigned char ) m_approach [ a ] . hereToNextHow ;
filesystem - > Write ( & type , sizeof ( unsigned char ) , file ) ;
}
//
// Save encounter spots for this area
//
{
// save number of encounter paths for this area
unsigned int count = m_spotEncounterList . Count ( ) ;
filesystem - > Write ( & count , sizeof ( unsigned int ) , file ) ;
SpotEncounter * e ;
FOR_EACH_LL ( m_spotEncounterList , it )
{
e = m_spotEncounterList [ it ] ;
if ( e - > from . area )
filesystem - > Write ( & e - > from . area - > m_id , sizeof ( unsigned int ) , file ) ;
else
filesystem - > Write ( & zero , sizeof ( unsigned int ) , file ) ;
unsigned char dir = ( unsigned char ) e - > fromDir ;
filesystem - > Write ( & dir , sizeof ( unsigned char ) , file ) ;
if ( e - > to . area )
filesystem - > Write ( & e - > to . area - > m_id , sizeof ( unsigned int ) , file ) ;
else
filesystem - > Write ( & zero , sizeof ( unsigned int ) , file ) ;
dir = ( unsigned char ) e - > toDir ;
filesystem - > Write ( & dir , sizeof ( unsigned char ) , file ) ;
// write list of spots along this path
unsigned char spotCount ;
if ( e - > spotList . Count ( ) > 255 )
{
spotCount = 255 ;
Warning ( " Warning: NavArea #%d: Truncated encounter spot list to 255 \n " , m_id ) ;
}
else
{
spotCount = ( unsigned char ) e - > spotList . Count ( ) ;
}
filesystem - > Write ( & spotCount , sizeof ( unsigned char ) , file ) ;
saveCount = 0 ;
FOR_EACH_LL ( e - > spotList , sit )
{
SpotOrder * order = & e - > spotList [ 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 ;
filesystem - > Write ( & id , sizeof ( unsigned int ) , file ) ;
unsigned char t = ( unsigned char ) ( 255 * order - > t ) ;
filesystem - > Write ( & t , sizeof ( unsigned char ) , file ) ;
// overflow check
if ( + + saveCount = = spotCount )
break ;
}
}
}
// store place dictionary entry
PlaceDirectory : : IndexType entry = placeDirectory . GetIndex ( GetPlace ( ) ) ;
filesystem - > Write ( & entry , sizeof ( entry ) , file ) ;
// 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 ( ) ;
filesystem - > Write ( & count , sizeof ( unsigned int ) , file ) ;
NavLadderConnect ladder ;
FOR_EACH_LL ( m_ladder [ i ] , it )
{
ladder = m_ladder [ i ] [ it ] ;
unsigned int id = ladder . ladder - > GetID ( ) ;
filesystem - > Write ( & id , sizeof ( id ) , file ) ;
}
}
// save earliest occupy times
for ( i = 0 ; i < MAX_NAV_TEAMS ; + + i )
{
// no spot in the map should take longer than this to reach
filesystem - > Write ( & m_earliestOccupyTime [ i ] , sizeof ( m_earliestOccupyTime [ i ] ) , file ) ;
}
}
//--------------------------------------------------------------------------------------------------------------
/**
* Load a navigation area from the file
*/
void CNavArea : : Load ( FileHandle_t file , unsigned int version )
{
// load ID
filesystem - > Read ( & m_id , sizeof ( unsigned int ) , file ) ;
// update nextID to avoid collisions
if ( m_id > = m_nextID )
m_nextID = m_id + 1 ;
// load attribute flags
if ( version < = 8 )
{
unsigned char flags = 0 ;
filesystem - > Read ( & flags , sizeof ( unsigned char ) , file ) ;
m_attributeFlags = flags ;
}
else
{
filesystem - > Read ( & m_attributeFlags , sizeof ( unsigned short ) , file ) ;
}
// load extent of area
filesystem - > Read ( & m_extent , 6 * sizeof ( float ) , file ) ;
m_center . x = ( m_extent . lo . x + m_extent . hi . x ) / 2.0f ;
m_center . y = ( m_extent . lo . y + m_extent . hi . y ) / 2.0f ;
m_center . z = ( m_extent . lo . z + m_extent . hi . z ) / 2.0f ;
// load heights of implicit corners
filesystem - > Read ( & m_neZ , sizeof ( float ) , file ) ;
filesystem - > Read ( & m_swZ , sizeof ( float ) , file ) ;
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 ;
int result = filesystem - > Read ( & count , sizeof ( unsigned int ) , file ) ;
Assert ( result = = sizeof ( unsigned int ) ) ;
for ( unsigned int i = 0 ; i < count ; + + i )
{
NavConnect connect ;
result = filesystem - > Read ( & connect . id , sizeof ( unsigned int ) , file ) ;
Assert ( result = = sizeof ( unsigned int ) ) ;
// 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 ;
filesystem - > Read ( & hidingSpotCount , sizeof ( unsigned char ) , file ) ;
if ( version = = 1 )
{
// load simple vector array
Vector pos ;
for ( int h = 0 ; h < hidingSpotCount ; + + h )
{
filesystem - > Read ( & pos , 3 * sizeof ( float ) , file ) ;
// create new hiding spot and put on master list
HidingSpot * spot = TheNavMesh - > CreateHidingSpot ( ) ;
spot - > SetPosition ( pos ) ;
spot - > SetFlags ( HidingSpot : : IN_COVER ) ;
m_hidingSpotList . 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 ( file , version ) ;
m_hidingSpotList . AddToTail ( spot ) ;
}
}
//
// Load number of approach areas
//
filesystem - > Read ( & m_approachCount , sizeof ( unsigned char ) , file ) ;
// load approach area info (IDs)
unsigned char type ;
for ( int a = 0 ; a < m_approachCount ; + + a )
{
filesystem - > Read ( & m_approach [ a ] . here . id , sizeof ( unsigned int ) , file ) ;
filesystem - > Read ( & m_approach [ a ] . prev . id , sizeof ( unsigned int ) , file ) ;
filesystem - > Read ( & type , sizeof ( unsigned char ) , file ) ;
m_approach [ a ] . prevToHereHow = ( NavTraverseType ) type ;
filesystem - > Read ( & m_approach [ a ] . next . id , sizeof ( unsigned int ) , file ) ;
filesystem - > Read ( & type , sizeof ( unsigned char ) , file ) ;
m_approach [ a ] . hereToNextHow = ( NavTraverseType ) type ;
}
//
// Load encounter paths for this area
//
unsigned int count ;
filesystem - > Read ( & count , sizeof ( unsigned int ) , file ) ;
if ( version < 3 )
{
// old data, read and discard
for ( unsigned int e = 0 ; e < count ; + + e )
{
SpotEncounter encounter ;
filesystem - > Read ( & encounter . from . id , sizeof ( unsigned int ) , file ) ;
filesystem - > Read ( & encounter . to . id , sizeof ( unsigned int ) , file ) ;
filesystem - > Read ( & encounter . path . from . x , 3 * sizeof ( float ) , file ) ;
filesystem - > Read ( & encounter . path . to . x , 3 * sizeof ( float ) , file ) ;
// read list of spots along this path
unsigned char spotCount ;
filesystem - > Read ( & spotCount , sizeof ( unsigned char ) , file ) ;
for ( int s = 0 ; s < spotCount ; + + s )
{
Vector pos ;
filesystem - > Read ( & pos , 3 * sizeof ( float ) , file ) ;
filesystem - > Read ( & pos , sizeof ( float ) , file ) ;
}
}
return ;
}
for ( unsigned int e = 0 ; e < count ; + + e )
{
SpotEncounter * encounter = new SpotEncounter ;
filesystem - > Read ( & encounter - > from . id , sizeof ( unsigned int ) , file ) ;
unsigned char dir ;
filesystem - > Read ( & dir , sizeof ( unsigned char ) , file ) ;
encounter - > fromDir = static_cast < NavDirType > ( dir ) ;
filesystem - > Read ( & encounter - > to . id , sizeof ( unsigned int ) , file ) ;
filesystem - > Read ( & dir , sizeof ( unsigned char ) , file ) ;
encounter - > toDir = static_cast < NavDirType > ( dir ) ;
// read list of spots along this path
unsigned char spotCount ;
filesystem - > Read ( & spotCount , sizeof ( unsigned char ) , file ) ;
SpotOrder order ;
for ( int s = 0 ; s < spotCount ; + + s )
{
filesystem - > Read ( & order . id , sizeof ( unsigned int ) , file ) ;
unsigned char t ;
filesystem - > Read ( & t , sizeof ( unsigned char ) , file ) ;
order . t = ( float ) t / 255.0f ;
encounter - > spotList . AddToTail ( order ) ;
}
m_spotEncounterList . AddToTail ( encounter ) ;
}
if ( version < 5 )
return ;
//
// Load Place data
//
PlaceDirectory : : IndexType entry ;
filesystem - > Read ( & entry , sizeof ( entry ) , file ) ;
// convert entry to actual Place
SetPlace ( placeDirectory . IndexToPlace ( entry ) ) ;
if ( version < 7 )
return ;
// load ladder data
for ( int dir = 0 ; dir < CNavLadder : : NUM_LADDER_DIRECTIONS ; + + dir )
{
filesystem - > Read ( & count , sizeof ( unsigned int ) , file ) ;
{
for ( unsigned int i = 0 ; i < count ; + + i )
{
NavLadderConnect connect ;
filesystem - > Read ( & connect . id , sizeof ( unsigned int ) , file ) ;
bool alreadyConnected = false ;
FOR_EACH_LL ( 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 ;
// 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
filesystem - > Read ( & m_earliestOccupyTime [ i ] , sizeof ( m_earliestOccupyTime [ i ] ) , file ) ;
}
}
//--------------------------------------------------------------------------------------------------------------
/**
* 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_LL ( 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_LL ( m_connect [ d ] , it )
{
NavConnect * connect = & m_connect [ d ] [ it ] ;
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 ;
}
}
}
// resolve approach area IDs
for ( int a = 0 ; a < m_approachCount ; + + a )
{
m_approach [ a ] . here . area = TheNavMesh - > GetNavAreaByID ( m_approach [ a ] . here . id ) ;
if ( m_approach [ a ] . here . id & & m_approach [ a ] . here . area = = NULL )
{
Msg ( " CNavArea::PostLoad: Corrupt navigation data. Missing Approach Area (here). \n " ) ;
error = NAV_CORRUPT_DATA ;
}
m_approach [ a ] . prev . area = TheNavMesh - > GetNavAreaByID ( m_approach [ a ] . prev . id ) ;
if ( m_approach [ a ] . prev . id & & m_approach [ a ] . prev . area = = NULL )
{
Msg ( " CNavArea::PostLoad: Corrupt navigation data. Missing Approach Area (prev). \n " ) ;
error = NAV_CORRUPT_DATA ;
}
m_approach [ a ] . next . area = TheNavMesh - > GetNavAreaByID ( m_approach [ a ] . next . id ) ;
if ( m_approach [ a ] . next . id & & m_approach [ a ] . next . area = = NULL )
{
Msg ( " CNavArea::PostLoad: Corrupt navigation data. Missing Approach Area (next). \n " ) ;
error = NAV_CORRUPT_DATA ;
}
}
// resolve spot encounter IDs
SpotEncounter * e ;
FOR_EACH_LL ( m_spotEncounterList , it )
{
e = m_spotEncounterList [ 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_LL ( e - > spotList , sit )
{
SpotOrder * order = & e - > spotList [ sit ] ;
order - > spot = GetHidingSpotByID ( order - > id ) ;
if ( order - > spot = = NULL )
{
Msg ( " CNavArea::PostLoad: Corrupt navigation data. Missing Hiding Spot \n " ) ;
error = NAV_CORRUPT_DATA ;
}
}
}
// build overlap list
/// @todo Optimize this
FOR_EACH_LL ( TheNavAreaList , nit )
{
CNavArea * area = TheNavAreaList [ nit ] ;
if ( area = = this )
continue ;
if ( IsOverlapping ( area ) )
m_overlapList . AddToTail ( area ) ;
}
return error ;
}
//--------------------------------------------------------------------------------------------------------------
/**
* 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 ) ;
static char filename [ 256 ] ;
Q_snprintf ( filename , sizeof ( filename ) , " %s \\ maps \\ %s.nav " , 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 ( void )
{
// 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.
{
bool hasApproachAreas = false ;
bool hasSpotEncounters = false ;
FOR_EACH_LL ( TheNavAreaList , it )
{
CNavArea * area = TheNavAreaList [ it ] ;
if ( area - > GetApproachInfoCount ( ) )
{
hasApproachAreas = true ;
}
if ( area - > GetSpotEncounterCount ( ) )
{
hasSpotEncounters = true ;
}
}
if ( ! hasApproachAreas | | ! hasSpotEncounters )
{
Warning ( " The nav mesh needs a full nav_analyze \n " ) ;
}
}
}
/**
* Store Navigation Mesh to a file
*/
bool CNavMesh : : Save ( void ) const
{
WarnIfMeshNeedsAnalysis ( ) ;
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 ;
}
FileHandle_t file = filesystem - > Open ( filename , " wb " ) ;
if ( ! file )
{
return false ;
}
// store "magic number" to help identify this kind of file
unsigned int magic = NAV_MAGIC_NUMBER ;
filesystem - > Write ( & magic , sizeof ( unsigned int ) , file ) ;
// 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
unsigned int version = NavCurrentVersion ;
filesystem - > Write ( & version , sizeof ( unsigned int ) , file ) ;
// 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 ) ;
filesystem - > Write ( & bspSize , sizeof ( unsigned int ) , file ) ;
//
// Build a directory of the Places in this map
//
placeDirectory . Reset ( ) ;
FOR_EACH_LL ( TheNavAreaList , nit )
{
CNavArea * area = TheNavAreaList [ nit ] ;
Place place = area - > GetPlace ( ) ;
if ( place )
{
placeDirectory . AddPlace ( place ) ;
}
}
placeDirectory . Save ( file ) ;
//
// Store navigation areas
//
{
// store number of areas
unsigned int count = TheNavAreaList . Count ( ) ;
filesystem - > Write ( & count , sizeof ( unsigned int ) , file ) ;
// store each area
FOR_EACH_LL ( TheNavAreaList , it )
{
CNavArea * area = TheNavAreaList [ it ] ;
area - > Save ( file , version ) ;
}
}
//
// Store ladders
//
{
// store number of ladders
unsigned int count = m_ladderList . Count ( ) ;
filesystem - > Write ( & count , sizeof ( unsigned int ) , file ) ;
// store each ladder
FOR_EACH_LL ( m_ladderList , it )
{
CNavLadder * ladder = m_ladderList [ it ] ;
ladder - > Save ( file , version ) ;
}
}
filesystem - > Flush ( file ) ;
filesystem - > Close ( file ) ;
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 bspPathname [ 256 ] ;
char filename [ 256 ] ;
Q_strncpy ( bspPathname , " maps/ " , sizeof ( bspPathname ) ) ;
Q_strncat ( bspPathname , bspFilename , sizeof ( bspPathname ) , COPY_ALL_CHARACTERS ) ;
Q_strncpy ( filename , bspPathname , sizeof ( filename ) ) ;
Q_SetExtension ( filename , " .nav " , sizeof ( filename ) ) ;
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 ;
2008-09-15 02:50:57 -05:00
default :
break ;
2008-09-15 01:07:45 -05:00
}
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 ) ;
//--------------------------------------------------------------------------------------------------------------
/**
* Load AI navigation data from a file
*/
NavErrorType CNavMesh : : Load ( void )
{
// free previous navigation mesh data
Reset ( ) ;
placeDirectory . Reset ( ) ;
CNavArea : : m_nextID = 1 ;
// nav filename is derived from map filename
char filename [ 256 ] ;
Q_snprintf ( filename , sizeof ( filename ) , " maps \\ %s.nav " , STRING ( gpGlobals - > mapname ) ) ;
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 )
{
Msg ( " Invalid navigation file '%s'. \n " , filename ) ;
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 )
{
Msg ( " Unknown navigation file version. \n " ) ;
filesystem - > Close ( file ) ;
return NAV_BAD_FILE_VERSION ;
}
if ( version > = 4 )
{
// 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
char * bspFilename = GetBspFilename ( filename ) ;
if ( bspFilename = = NULL )
{
filesystem - > Close ( file ) ;
return NAV_INVALID_FILE ;
}
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
Msg ( " The Navigation Mesh was built using a different version of this map. \n " ) ;
}
else
{
Warning ( " The Navigation Mesh was built using a different version of this map. \n " ) ;
}
m_isFromCurrentMap = false ;
}
}
// load Place directory
if ( version > = 5 )
{
placeDirectory . Load ( file ) ;
}
// get number of areas
unsigned int count ;
unsigned int i ;
result = filesystem - > Read ( & count , sizeof ( unsigned int ) , 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
for ( i = 0 ; i < count ; + + i )
{
CNavArea * area = new CNavArea ;
area - > Load ( file , version ) ;
TheNavAreaList . AddToTail ( area ) ;
const Extent & areaExtent = area - > GetExtent ( ) ;
// check validity of nav area
if ( areaExtent . lo . x > = areaExtent . hi . x | | areaExtent . lo . y > = areaExtent . hi . y )
Warning ( " WARNING: Degenerate Navigation Area #%d at ( %g, %g, %g ) \n " ,
area - > GetID ( ) , area - > m_center . x , area - > m_center . y , area - > m_center . z ) ;
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_LL ( TheNavAreaList , it )
{
AddNavArea ( TheNavAreaList [ it ] ) ;
}
//
// Set up all the ladders
//
if ( version > = 6 )
{
result = filesystem - > Read ( & count , sizeof ( unsigned int ) , file ) ;
// load the ladders
for ( i = 0 ; i < count ; + + i )
{
CNavLadder * ladder = new CNavLadder ;
ladder - > Load ( file , version ) ;
m_ladderList . AddToTail ( ladder ) ;
}
}
else
{
BuildLadders ( ) ;
}
// allow areas to connect to each other, etc
FOR_EACH_LL ( TheNavAreaList , pit )
{
CNavArea * area = TheNavAreaList [ pit ] ;
area - > PostLoad ( ) ;
}
// allow hiding spots to compute information
FOR_EACH_LL ( TheHidingSpotList , hit )
{
HidingSpot * spot = TheHidingSpotList [ hit ] ;
spot - > PostLoad ( ) ;
}
if ( version < 8 )
{
// Old nav meshes need to compute earliest occupy times
FOR_EACH_LL ( TheNavAreaList , nit )
{
CNavArea * area = TheNavAreaList [ nit ] ;
area - > ComputeEarliestOccupyTimes ( ) ;
}
}
ComputeBattlefrontAreas ( ) ;
// the Navigation Mesh has been successfully loaded
m_isLoaded = true ;
filesystem - > Close ( file ) ;
WarnIfMeshNeedsAnalysis ( ) ;
return NAV_OK ;
}