You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
505 lines
12 KiB
505 lines
12 KiB
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
|
//
|
|
// Purpose:
|
|
//
|
|
// $NoKeywords: $
|
|
//
|
|
//=============================================================================//
|
|
// nav_area.cpp
|
|
// AI Navigation areas
|
|
// Author: Michael S. Booth ([email protected]), January 2003
|
|
|
|
#include "cbase.h"
|
|
#include "cs_nav_mesh.h"
|
|
#include "cs_nav_area.h"
|
|
#include "cs_gamerules.h"
|
|
#include "nav_pathfind.h"
|
|
#include "nav_colors.h"
|
|
#include "fmtstr.h"
|
|
#include "props_shared.h"
|
|
#include "func_breakablesurf.h"
|
|
#include "color.h"
|
|
#include "collisionutils.h"
|
|
#include "point_hiding_spot.h"
|
|
|
|
// memdbgon must be the last include file in a .cpp file!!!
|
|
#include <tier0/memdbgon.h>
|
|
|
|
#ifdef _WIN32
|
|
#pragma warning (disable:4701) // disable warning that variable *may* not be initialized
|
|
#endif
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------------------
|
|
/**
|
|
* Constructor used during normal runtime.
|
|
*/
|
|
CCSNavArea::CCSNavArea( void )
|
|
{
|
|
#ifndef PLATFORM_64BITS // TODO64: this is probably just alignment for performance, but perhaps the sizes are hardcoded somewhere
|
|
COMPILE_TIME_ASSERT( sizeof( CNavAreaCriticalData ) == 128 );
|
|
COMPILE_TIME_ASSERT( sizeof( CCSNavArea ) == 768 );
|
|
#endif
|
|
m_approachCount = 0;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------------------
|
|
/**
|
|
* Destructor
|
|
*/
|
|
CCSNavArea::~CCSNavArea()
|
|
{
|
|
}
|
|
|
|
|
|
void CCSNavArea::OnServerActivate( void )
|
|
{
|
|
CNavArea::OnServerActivate();
|
|
|
|
}
|
|
|
|
void CCSNavArea::OnRoundRestart( void )
|
|
{
|
|
CNavArea::OnRoundRestart();
|
|
}
|
|
|
|
|
|
void CCSNavArea::Save( CUtlBuffer &fileBuffer, unsigned int version ) const
|
|
{
|
|
CNavArea::Save( fileBuffer, version );
|
|
|
|
//
|
|
// Save the approach areas for this area
|
|
//
|
|
|
|
// save number of approach areas
|
|
fileBuffer.PutUnsignedChar(m_approachCount);
|
|
|
|
// save approach area info
|
|
for( int a=0; a<m_approachCount; ++a )
|
|
{
|
|
if (m_approach[a].here.area)
|
|
fileBuffer.PutUnsignedInt(m_approach[a].here.area->GetID());
|
|
else
|
|
fileBuffer.PutUnsignedInt(0);
|
|
|
|
if (m_approach[a].prev.area)
|
|
fileBuffer.PutUnsignedInt(m_approach[a].prev.area->GetID());
|
|
else
|
|
fileBuffer.PutUnsignedInt(0);
|
|
fileBuffer.PutUnsignedChar(m_approach[a].prevToHereHow);
|
|
|
|
if (m_approach[a].next.area)
|
|
fileBuffer.PutUnsignedInt(m_approach[a].next.area->GetID());
|
|
else
|
|
fileBuffer.PutUnsignedInt(0);
|
|
fileBuffer.PutUnsignedChar(m_approach[a].hereToNextHow);
|
|
}
|
|
}
|
|
|
|
NavErrorType CCSNavArea::Load( CUtlBuffer &fileBuffer, unsigned int version, unsigned int subVersion )
|
|
{
|
|
if ( version < 15 )
|
|
return LoadLegacy(fileBuffer, version, subVersion);
|
|
|
|
// load base class data
|
|
NavErrorType error = CNavArea::Load( fileBuffer, version, subVersion );
|
|
|
|
switch ( subVersion )
|
|
{
|
|
case 1:
|
|
//
|
|
// Load number of approach areas
|
|
//
|
|
m_approachCount = fileBuffer.GetUnsignedChar();
|
|
|
|
// load approach area info (IDs)
|
|
for( int a = 0; a < m_approachCount; ++a )
|
|
{
|
|
m_approach[a].here.id = fileBuffer.GetUnsignedInt();
|
|
|
|
m_approach[a].prev.id = fileBuffer.GetUnsignedInt();
|
|
m_approach[a].prevToHereHow = (NavTraverseType)fileBuffer.GetUnsignedChar();
|
|
|
|
m_approach[a].next.id = fileBuffer.GetUnsignedInt();
|
|
m_approach[a].hereToNextHow = (NavTraverseType)fileBuffer.GetUnsignedChar();
|
|
}
|
|
|
|
if ( !fileBuffer.IsValid() )
|
|
error = NAV_INVALID_FILE;
|
|
|
|
// fall through
|
|
|
|
case 0:
|
|
// legacy version
|
|
break;
|
|
|
|
default:
|
|
Warning( "Unknown NavArea sub-version number\n" );
|
|
error = NAV_INVALID_FILE;
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
NavErrorType CCSNavArea::PostLoad( void )
|
|
{
|
|
NavErrorType error = CNavArea::PostLoad();
|
|
|
|
// 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;
|
|
}
|
|
}
|
|
return error;
|
|
}
|
|
|
|
|
|
void CCSNavArea::Draw( void ) const
|
|
{
|
|
CNavArea::Draw();
|
|
}
|
|
|
|
void CCSNavArea::CustomAnalysis( bool isIncremental /*= false */ )
|
|
{
|
|
ComputeApproachAreas();
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------------------
|
|
// return danger decay rate per second
|
|
float CCSNavArea::GetDangerDecayRate( void ) const
|
|
{
|
|
if ( CSGameRules()->IsPlayingGunGameTRBomb() )
|
|
{
|
|
// decay danger faster in this aggressive bomb planting mode
|
|
return 1.0f / 30.0f;
|
|
}
|
|
|
|
// one kill == 1.0, which we will forget about in two minutes
|
|
return 1.0f / 120.0f;
|
|
}
|
|
|
|
|
|
float CCSNavArea::GetEarliestOccupyTime( int teamID ) const
|
|
{
|
|
if ( CSGameRules()->IsPlayingCoopMission() )
|
|
return 0.0f;
|
|
|
|
return BaseClass::GetEarliestOccupyTime( teamID );
|
|
}
|
|
|
|
void CCSNavArea::UpdateBlocked( bool force /*= false*/, int teamID /*= TEAM_ANY */ )
|
|
{
|
|
if ( CSGameRules() && CSGameRules()->IsPlayingCoopMission() )
|
|
BaseClass::UpdateBlocked( force, teamID );
|
|
}
|
|
|
|
bool CCSNavArea::IsBlocked( int teamID, bool ignoreNavBlockers /*= false */ ) const
|
|
{
|
|
return ( CSGameRules() && CSGameRules()->IsPlayingCoopMission() ) ? BaseClass::IsBlocked( teamID, ignoreNavBlockers ) : false;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------------------
|
|
/**
|
|
* Load legacy navigation area from the file
|
|
*/
|
|
NavErrorType CCSNavArea::LoadLegacy( 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) );
|
|
|
|
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_nwCorner.x, m_nwCorner.y, m_nwCorner.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 )
|
|
{
|
|
//
|
|
// Load number of approach areas
|
|
//
|
|
m_approachCount = fileBuffer.GetUnsignedChar();
|
|
|
|
// load approach area info (IDs)
|
|
for( int a = 0; a < m_approachCount; ++a )
|
|
{
|
|
m_approach[a].here.id = fileBuffer.GetUnsignedInt();
|
|
|
|
m_approach[a].prev.id = fileBuffer.GetUnsignedInt();
|
|
m_approach[a].prevToHereHow = (NavTraverseType)fileBuffer.GetUnsignedChar();
|
|
|
|
m_approach[a].next.id = fileBuffer.GetUnsignedInt();
|
|
m_approach[a].hereToNextHow = (NavTraverseType)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();
|
|
|
|
m_potentiallyVisibleAreas.EnsureCapacity( 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;
|
|
}
|
|
|
|
|
|
CCSHidingSpot::~CCSHidingSpot()
|
|
{
|
|
if ( m_pOwningEntity )
|
|
m_pOwningEntity->DetachFromHidingSpot();
|
|
}
|