2013-12-02 19:31:46 -08:00
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: AI Utility classes for building the initial AI Networks
//
// $Workfile: $
// $Date: $
//
//-----------------------------------------------------------------------------
// $Log: $
//
// $NoKeywords: $
//=============================================================================//
# include "cbase.h"
# include "ai_node.h"
# include "ai_hull.h"
# include "ai_hint.h"
# include "ai_initutils.h"
# include "ai_networkmanager.h"
// to help eliminate node clutter by level designers, this is used to cap how many other nodes
// any given node is allowed to 'see' in the first stage of graph creation "LinkVisibleNodes()".
# include "ai_network.h"
// memdbgon must be the last include file in a .cpp file!!!
# include "tier0/memdbgon.h"
LINK_ENTITY_TO_CLASS ( info_hint , CNodeEnt ) ;
LINK_ENTITY_TO_CLASS ( info_node , CNodeEnt ) ;
LINK_ENTITY_TO_CLASS ( info_node_hint , CNodeEnt ) ;
LINK_ENTITY_TO_CLASS ( info_node_air , CNodeEnt ) ;
LINK_ENTITY_TO_CLASS ( info_node_air_hint , CNodeEnt ) ;
LINK_ENTITY_TO_CLASS ( info_node_climb , CNodeEnt ) ;
LINK_ENTITY_TO_CLASS ( aitesthull , CAI_TestHull ) ;
//-----------------------------------------------------------------------------
// Init static variables
//-----------------------------------------------------------------------------
CAI_TestHull * CAI_TestHull : : pTestHull = NULL ;
# ifdef CSTRIKE_DLL
# define PLAYER_MODEL "models / player / ct_urban.mdl"
# else
# define PLAYER_MODEL "models / player.mdl"
# endif
//-----------------------------------------------------------------------------
// Purpose: Make sure we have a "player.mdl" hull to test with
//-----------------------------------------------------------------------------
void CAI_TestHull : : Precache ( )
{
BaseClass : : Precache ( ) ;
PrecacheModel ( PLAYER_MODEL ) ;
}
//=========================================================
// CAI_TestHull::Spawn
//=========================================================
void CAI_TestHull : : Spawn ( void )
{
Precache ( ) ;
SetModel ( PLAYER_MODEL ) ;
// Set an initial hull size (this will change later)
SetHullType ( HULL_HUMAN ) ;
SetHullSizeNormal ( ) ;
SetSolid ( SOLID_BBOX ) ;
AddSolidFlags ( FSOLID_NOT_SOLID ) ;
SetMoveType ( MOVETYPE_STEP ) ;
m_iHealth = 50 ;
bInUse = false ;
// Make this invisible
AddEffects ( EF_NODRAW ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Get the test hull (create if none)
// Input :
// Output :
//-----------------------------------------------------------------------------
CAI_TestHull * CAI_TestHull : : GetTestHull ( void )
{
if ( ! CAI_TestHull : : pTestHull )
{
CAI_TestHull : : pTestHull = CREATE_ENTITY ( CAI_TestHull , " aitesthull " ) ;
CAI_TestHull : : pTestHull - > Spawn ( ) ;
CAI_TestHull : : pTestHull - > AddFlag ( FL_NPC ) ;
}
if ( CAI_TestHull : : pTestHull - > bInUse = = true )
{
DevMsg ( " WARNING: TestHull used and never returned! \n " ) ;
Assert ( 0 ) ;
}
CAI_TestHull : : pTestHull - > RemoveSolidFlags ( FSOLID_NOT_SOLID ) ;
CAI_TestHull : : pTestHull - > bInUse = true ;
return CAI_TestHull : : pTestHull ;
}
//-----------------------------------------------------------------------------
// Purpose: Get the test hull (create if none)
// Input :
// Output :
//-----------------------------------------------------------------------------
void CAI_TestHull : : ReturnTestHull ( void )
{
CAI_TestHull : : pTestHull - > bInUse = false ;
CAI_TestHull : : pTestHull - > AddSolidFlags ( FSOLID_NOT_SOLID ) ;
UTIL_SetSize ( CAI_TestHull : : pTestHull , vec3_origin , vec3_origin ) ;
UTIL_RemoveImmediate ( pTestHull ) ;
pTestHull = NULL ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : &startPos -
// &endPos -
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CAI_TestHull : : IsJumpLegal ( const Vector & startPos , const Vector & apex , const Vector & endPos ) const
{
const float MAX_JUMP_RISE = 1024.0f ;
const float MAX_JUMP_DISTANCE = 1024.0f ;
const float MAX_JUMP_DROP = 1024.0f ;
return BaseClass : : IsJumpLegal ( startPos , apex , endPos , MAX_JUMP_RISE , MAX_JUMP_DISTANCE , MAX_JUMP_DROP ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input :
// Output :
//-----------------------------------------------------------------------------
CAI_TestHull : : ~ CAI_TestHull ( void )
{
CAI_TestHull : : pTestHull = NULL ;
}
//###########################################################
// > CNodeEnt
//
// nodes start out as ents in the world. As they are spawned,
// the node info is recorded then the ents are discarded.
//###########################################################
//----------------------------------------------------
// Static vars
//----------------------------------------------------
int CNodeEnt : : m_nNodeCount = 0 ;
// -------------
// Data table
// -------------
BEGIN_SIMPLE_DATADESC ( HintNodeData )
DEFINE_FIELD ( strEntityName , FIELD_STRING ) ,
// DEFINE_FIELD( vecPosition, FIELD_VECTOR ), // Don't save
DEFINE_KEYFIELD ( nHintType , FIELD_SHORT , " hinttype " ) ,
DEFINE_KEYFIELD ( strGroup , FIELD_STRING , " Group " ) ,
DEFINE_KEYFIELD ( iDisabled , FIELD_INTEGER , " StartHintDisabled " ) ,
DEFINE_FIELD ( nNodeID , FIELD_INTEGER ) ,
DEFINE_KEYFIELD ( iszActivityName , FIELD_STRING , " hintactivity " ) ,
DEFINE_KEYFIELD ( nTargetWCNodeID , FIELD_INTEGER , " TargetNode " ) ,
DEFINE_KEYFIELD ( nWCNodeID , FIELD_INTEGER , " nodeid " ) ,
DEFINE_KEYFIELD ( fIgnoreFacing , FIELD_INTEGER , " IgnoreFacing " ) ,
DEFINE_KEYFIELD ( minState , FIELD_INTEGER , " MinimumState " ) ,
DEFINE_KEYFIELD ( maxState , FIELD_INTEGER , " MaximumState " ) ,
END_DATADESC ( )
// -------------
// Data table
// -------------
BEGIN_DATADESC ( CNodeEnt )
DEFINE_EMBEDDED ( m_NodeData ) ,
END_DATADESC ( )
//=========================================================
//=========================================================
void CNodeEnt : : Spawn ( void )
{
Spawn ( NULL ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pMapData -
//-----------------------------------------------------------------------------
int CNodeEnt : : Spawn ( const char * pMapData )
{
m_NodeData . strEntityName = GetEntityName ( ) ;
m_NodeData . vecPosition = GetAbsOrigin ( ) ;
m_NodeData . nNodeID = NO_NODE ;
if ( m_NodeData . minState = = NPC_STATE_NONE )
m_NodeData . minState = NPC_STATE_IDLE ;
if ( m_NodeData . maxState = = NPC_STATE_NONE )
m_NodeData . maxState = NPC_STATE_COMBAT ;
// ---------------------------------------------------------------------------------
// If just a hint node (not used for navigation) just create a hint and bail
// ---------------------------------------------------------------------------------
if ( FClassnameIs ( this , " info_hint " ) )
{
if ( m_NodeData . nHintType )
{
CAI_HintManager : : CreateHint ( & m_NodeData , pMapData ) ;
}
else
{
Warning ( " info_hint (HammerID: %d, position (%.2f, %.2f, %.2f)) with no hint type. \n " , m_NodeData . nWCNodeID , m_NodeData . vecPosition . x , m_NodeData . vecPosition . y , m_NodeData . vecPosition . z ) ;
}
UTIL_RemoveImmediate ( this ) ;
return - 1 ;
}
// ---------------------------------------------------------------------------------
// First check if this node has a hint. If so create a hint entity
// ---------------------------------------------------------------------------------
CAI_Hint * pHint = NULL ;
2021-05-11 10:10:44 -05:00
if ( ClassMatches ( " info_node_hint " ) | | ClassMatches ( " info_node_air_hint " )
# ifdef MAPBASE
| | ClassMatches ( " info_node_climb " ) // Climb nodes contain hint data in the FGD
# endif
)
2013-12-02 19:31:46 -08:00
{
if ( m_NodeData . nHintType | | m_NodeData . strGroup ! = NULL_STRING | | m_NodeData . strEntityName ! = NULL_STRING )
{
m_NodeData . nNodeID = m_nNodeCount ;
pHint = CAI_HintManager : : CreateHint ( & m_NodeData , pMapData ) ;
pHint - > AddSpawnFlags ( GetSpawnFlags ( ) ) ;
}
}
// ---------------------------------------------------------------------------------
// If we loaded from disk, we can discard all these node ents as soon as they spawn
// unless we are in WC edited mode
// ---------------------------------------------------------------------------------
if ( g_pAINetworkManager - > NetworksLoaded ( ) & & ! engine - > IsInEditMode ( ) )
{
// If hint exists for this node, set it
if ( pHint )
{
CAI_Node * pNode = g_pBigAINet - > GetNode ( m_nNodeCount ) ;
if ( pNode )
pNode - > SetHint ( pHint ) ;
else
{
DevMsg ( " AI node graph corrupt \n " ) ;
}
}
m_nNodeCount + + ;
UTIL_RemoveImmediate ( this ) ;
return - 1 ;
}
else
{
m_nNodeCount + + ;
}
// ---------------------------------------------------------------------------------
// Add a new node to the network
// ---------------------------------------------------------------------------------
// For now just using one big AI network
CAI_Node * new_node = g_pBigAINet - > AddNode ( GetAbsOrigin ( ) , GetAbsAngles ( ) . y ) ;
new_node - > SetHint ( pHint ) ;
// -------------------------------------------------------------------------
// Update table of how each WC id relates to each engine ID
// -------------------------------------------------------------------------
if ( g_pAINetworkManager - > GetEditOps ( ) - > m_pNodeIndexTable )
{
g_pAINetworkManager - > GetEditOps ( ) - > m_pNodeIndexTable [ new_node - > GetId ( ) ] = m_NodeData . nWCNodeID ;
}
// Keep track of largest index used by WC
if ( g_pAINetworkManager - > GetEditOps ( ) - > m_nNextWCIndex < = m_NodeData . nWCNodeID )
{
g_pAINetworkManager - > GetEditOps ( ) - > m_nNextWCIndex = m_NodeData . nWCNodeID + 1 ;
}
// -------------------------------------------------------------------------
// If in WC edit mode:
// Remember the original positions of the nodes before
// they drop so we can send the undropped positions to wc.
// -------------------------------------------------------------------------
if ( engine - > IsInEditMode ( ) )
{
if ( g_pAINetworkManager - > GetEditOps ( ) - > m_pWCPosition )
{
g_pAINetworkManager - > GetEditOps ( ) - > m_pWCPosition [ new_node - > GetId ( ) ] = new_node - > GetOrigin ( ) ;
}
}
if ( FClassnameIs ( this , " info_node_air " ) | | FClassnameIs ( this , " info_node_air_hint " ) )
{
new_node - > SetType ( NODE_AIR ) ;
}
else if ( FClassnameIs ( this , " info_node_climb " ) )
{
new_node - > SetType ( NODE_CLIMB ) ;
}
else
{
new_node - > SetType ( NODE_GROUND ) ;
}
new_node - > m_eNodeInfo = ( m_spawnflags < < NODE_ENT_FLAGS_SHIFT ) ;
// If changed as part of WC editing process note that network must be rebuilt
if ( m_debugOverlays & OVERLAY_WC_CHANGE_ENTITY )
{
g_pAINetworkManager - > GetEditOps ( ) - > SetRebuildFlags ( ) ;
new_node - > m_eNodeInfo | = bits_NODE_WC_CHANGED ;
// Initialize the new nodes position. The graph may not be rebuild
// right away but the node should at least be positioned correctly
g_AINetworkBuilder . InitNodePosition ( g_pBigAINet , new_node ) ;
}
UTIL_RemoveImmediate ( this ) ;
return - 1 ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input :
// Output :
//-----------------------------------------------------------------------------
CNodeEnt : : CNodeEnt ( void )
{
m_debugOverlays = 0 ;
}