aboutsummaryrefslogtreecommitdiff
path: root/mp/src/game/server/ai_dynamiclink.cpp
diff options
context:
space:
mode:
authorJørgen P. Tjernø <[email protected]>2013-12-02 19:31:46 -0800
committerJørgen P. Tjernø <[email protected]>2013-12-02 19:46:31 -0800
commitf56bb35301836e56582a575a75864392a0177875 (patch)
treede61ddd39de3e7df52759711950b4c288592f0dc /mp/src/game/server/ai_dynamiclink.cpp
parentMark some more files as text. (diff)
downloadsource-sdk-2013-f56bb35301836e56582a575a75864392a0177875.tar.xz
source-sdk-2013-f56bb35301836e56582a575a75864392a0177875.zip
Fix line endings. WHAMMY.
Diffstat (limited to 'mp/src/game/server/ai_dynamiclink.cpp')
-rw-r--r--mp/src/game/server/ai_dynamiclink.cpp1460
1 files changed, 730 insertions, 730 deletions
diff --git a/mp/src/game/server/ai_dynamiclink.cpp b/mp/src/game/server/ai_dynamiclink.cpp
index 89b6b760..5f5fba9c 100644
--- a/mp/src/game/server/ai_dynamiclink.cpp
+++ b/mp/src/game/server/ai_dynamiclink.cpp
@@ -1,730 +1,730 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose: A link that can be turned on and off. Unlike normal links
-// dyanimc links must be entities so they can receive messages.
-// They update the state of the actual links. Allows us to save
-// a lot of memory by not making all links into entities
-//
-// $NoKeywords: $
-//=============================================================================//
-
-#include "cbase.h"
-#include "collisionutils.h"
-#include "ai_dynamiclink.h"
-#include "ai_node.h"
-#include "ai_link.h"
-#include "ai_network.h"
-#include "ai_networkmanager.h"
-#include "saverestore_utlvector.h"
-#include "editor_sendcommand.h"
-#include "bitstring.h"
-#include "tier0/vprof.h"
-
-// memdbgon must be the last include file in a .cpp file!!!
-#include "tier0/memdbgon.h"
-
-//-----------------------------------------------------------------------------
-
-LINK_ENTITY_TO_CLASS(info_node_link_controller, CAI_DynamicLinkController);
-
-BEGIN_DATADESC( CAI_DynamicLinkController )
-
- DEFINE_KEYFIELD( m_nLinkState, FIELD_INTEGER, "initialstate" ),
- DEFINE_KEYFIELD( m_strAllowUse, FIELD_STRING, "AllowUse" ),
- DEFINE_KEYFIELD( m_bInvertAllow, FIELD_BOOLEAN, "InvertAllow" ),
- DEFINE_KEYFIELD( m_bUseAirLinkRadius, FIELD_BOOLEAN, "useairlinkradius" ),
- // m_ControlledLinks (rebuilt)
-
- DEFINE_INPUTFUNC( FIELD_VOID, "TurnOn", InputTurnOn ),
- DEFINE_INPUTFUNC( FIELD_VOID, "TurnOff", InputTurnOff ),
-
- DEFINE_INPUTFUNC( FIELD_STRING, "SetAllowed", InputSetAllowed ),
- DEFINE_INPUTFUNC( FIELD_BOOLEAN, "SetInvert", InputSetInvert ),
-
-END_DATADESC()
-
-void CAI_DynamicLinkController::GenerateLinksFromVolume()
-{
- Assert( m_ControlledLinks.Count() == 0 );
-
- int nNodes = g_pBigAINet->NumNodes();
- CAI_Node **ppNodes = g_pBigAINet->AccessNodes();
-
- float MinDistCareSq = 0;
- if (m_bUseAirLinkRadius)
- {
- MinDistCareSq = Square(MAX_AIR_NODE_LINK_DIST + 0.1);
- }
- else
- {
- MinDistCareSq = Square(MAX_NODE_LINK_DIST + 0.1);
- }
-
- const Vector &origin = WorldSpaceCenter();
- Vector vAbsMins, vAbsMaxs;
- CollisionProp()->WorldSpaceAABB( &vAbsMins, &vAbsMaxs );
- vAbsMins -= Vector( 1, 1, 1 );
- vAbsMaxs += Vector( 1, 1, 1 );
-
- for ( int i = 0; i < nNodes; i++ )
- {
- CAI_Node *pNode = ppNodes[i];
- const Vector &nodeOrigin = pNode->GetOrigin();
- if ( origin.DistToSqr(nodeOrigin) < MinDistCareSq )
- {
- int nLinks = pNode->NumLinks();
- for ( int j = 0; j < nLinks; j++ )
- {
- CAI_Link *pLink = pNode->GetLinkByIndex( j );
- int iLinkDest = pLink->DestNodeID( i );
- if ( iLinkDest > i )
- {
- const Vector &originOther = ppNodes[iLinkDest]->GetOrigin();
- if ( origin.DistToSqr(originOther) < MinDistCareSq )
- {
- if ( IsBoxIntersectingRay( vAbsMins, vAbsMaxs, nodeOrigin, originOther - nodeOrigin ) )
- {
- Assert( IsBoxIntersectingRay( vAbsMins, vAbsMaxs, originOther, nodeOrigin - originOther ) );
-
- CAI_DynamicLink *pLink = (CAI_DynamicLink *)CreateEntityByName( "info_node_link" );
- pLink->m_nSrcID = i;
- pLink->m_nDestID = iLinkDest;
- pLink->m_nSrcEditID = g_pAINetworkManager->GetEditOps()->GetWCIdFromNodeId( pLink->m_nSrcID );
- pLink->m_nDestEditID = g_pAINetworkManager->GetEditOps()->GetWCIdFromNodeId( pLink->m_nDestID );
- pLink->m_nLinkState = m_nLinkState;
- pLink->m_strAllowUse = m_strAllowUse;
- pLink->m_bInvertAllow = m_bInvertAllow;
- pLink->m_bFixedUpIds = true;
- pLink->m_bNotSaved = true;
-
- pLink->Spawn();
- m_ControlledLinks.AddToTail( pLink );
- }
- }
- }
- }
- }
- }
-}
-
-void CAI_DynamicLinkController::InputTurnOn( inputdata_t &inputdata )
-{
- for ( int i = 0; i < m_ControlledLinks.Count(); i++ )
- {
- if ( m_ControlledLinks[i] == NULL )
- {
- m_ControlledLinks.FastRemove(i);
- if ( i >= m_ControlledLinks.Count() )
- break;
- }
- m_ControlledLinks[i]->InputTurnOn( inputdata );
- }
-
- m_nLinkState = LINK_ON;
-}
-
-void CAI_DynamicLinkController::InputTurnOff( inputdata_t &inputdata )
-{
- for ( int i = 0; i < m_ControlledLinks.Count(); i++ )
- {
- if ( m_ControlledLinks[i] == NULL )
- {
- m_ControlledLinks.FastRemove(i);
- if ( i >= m_ControlledLinks.Count() )
- break;
- }
- m_ControlledLinks[i]->InputTurnOff( inputdata );
- }
-
- m_nLinkState = LINK_OFF;
-}
-
-void CAI_DynamicLinkController::InputSetAllowed( inputdata_t &inputdata )
-{
- m_strAllowUse = inputdata.value.StringID();
- for ( int i = 0; i < m_ControlledLinks.Count(); i++ )
- {
- if ( m_ControlledLinks[i] == NULL )
- {
- m_ControlledLinks.FastRemove(i);
- if ( i >= m_ControlledLinks.Count() )
- break;
- }
- m_ControlledLinks[i]->m_strAllowUse = m_strAllowUse;
- }
-}
-
-void CAI_DynamicLinkController::InputSetInvert( inputdata_t &inputdata )
-{
- m_bInvertAllow = inputdata.value.Bool();
- for ( int i = 0; i < m_ControlledLinks.Count(); i++ )
- {
- if ( m_ControlledLinks[i] == NULL )
- {
- m_ControlledLinks.FastRemove(i);
- if ( i >= m_ControlledLinks.Count() )
- break;
- }
- m_ControlledLinks[i]->m_bInvertAllow = m_bInvertAllow;
- }
-}
-
-//-----------------------------------------------------------------------------
-
-LINK_ENTITY_TO_CLASS(info_node_link, CAI_DynamicLink);
-
-BEGIN_DATADESC( CAI_DynamicLink )
-
-// m_pNextDynamicLink
-DEFINE_KEYFIELD( m_nLinkState, FIELD_INTEGER, "initialstate" ),
-DEFINE_KEYFIELD( m_nSrcEditID, FIELD_INTEGER, "startnode" ),
-DEFINE_KEYFIELD( m_nDestEditID, FIELD_INTEGER, "endnode" ),
-DEFINE_KEYFIELD( m_nLinkType, FIELD_INTEGER, "linktype" ),
-DEFINE_FIELD( m_bInvertAllow, FIELD_BOOLEAN ),
-// m_nSrcID (rebuilt)
-// m_nDestID (rebuilt)
-DEFINE_KEYFIELD( m_strAllowUse, FIELD_STRING, "AllowUse" ),
-// m_bFixedUpIds (part of rebuild)
-// m_bNotSaved (rebuilt)
-
-DEFINE_INPUTFUNC( FIELD_VOID, "TurnOn", InputTurnOn ),
-DEFINE_INPUTFUNC( FIELD_VOID, "TurnOff", InputTurnOff ),
-
-END_DATADESC()
-
-//-----------------------------------------------------------------------------
-// Init static variables
-//-----------------------------------------------------------------------------
-CAI_DynamicLink *CAI_DynamicLink::m_pAllDynamicLinks = NULL;
-bool CAI_DynamicLink::gm_bInitialized;
-
-
-//------------------------------------------------------------------------------
-
-void CAI_DynamicLink::GenerateControllerLinks()
-{
- CAI_DynamicLinkController *pController = NULL;
- while ( ( pController = gEntList.NextEntByClass( pController ) ) != NULL )
- {
- pController->GenerateLinksFromVolume();
- }
-
-}
-
-//------------------------------------------------------------------------------
-// Purpose : Initializes src and dest IDs for all dynamic links
-//
-// Input :
-// Output :
-//------------------------------------------------------------------------------
-void CAI_DynamicLink::InitDynamicLinks(void)
-{
- if (!g_pAINetworkManager->GetEditOps()->m_pNodeIndexTable)
- {
- Warning("ERROR: Trying initialize links with no WC ID table!\n");
- return;
- }
-
- if ( gm_bInitialized )
- return;
-
- gm_bInitialized = true;
-
- bool bUpdateZones = false;
-
- GenerateControllerLinks();
-
- CAI_DynamicLink* pDynamicLink = CAI_DynamicLink::m_pAllDynamicLinks;
-
- while (pDynamicLink)
- {
- // -------------------------------------------------------------
- // First convert this links WC IDs to engine IDs
- // -------------------------------------------------------------
- if ( !pDynamicLink->m_bFixedUpIds )
- {
- int nSrcID = g_pAINetworkManager->GetEditOps()->GetNodeIdFromWCId( pDynamicLink->m_nSrcEditID );
- if (nSrcID == -1)
- {
- DevMsg( "ERROR: Dynamic link source WC node %d not found\n", pDynamicLink->m_nSrcEditID );
- nSrcID = NO_NODE;
- }
-
- int nDestID = g_pAINetworkManager->GetEditOps()->GetNodeIdFromWCId( pDynamicLink->m_nDestEditID );
- if (nDestID == -1)
- {
- DevMsg( "ERROR: Dynamic link dest WC node %d not found\n", pDynamicLink->m_nDestEditID );
- nDestID = NO_NODE;
- }
-
- pDynamicLink->m_nSrcID = nSrcID;
- pDynamicLink->m_nDestID = nDestID;
- pDynamicLink->m_bFixedUpIds = true;
- }
-
- if ( pDynamicLink->m_nSrcID != NO_NODE && pDynamicLink->m_nDestID != NO_NODE )
- {
- if ( ( pDynamicLink->GetSpawnFlags() & bits_HULL_BITS_MASK ) != 0 )
- {
- CAI_Link *pLink = pDynamicLink->FindLink();
- if ( !pLink )
- {
- CAI_Node *pNode1, *pNode2;
-
- pNode1 = g_pBigAINet->GetNode( pDynamicLink->m_nSrcID );
- pNode2 = g_pBigAINet->GetNode( pDynamicLink->m_nDestID );
-
- if ( pNode1 && pNode2 )
- {
- pLink = g_pBigAINet->CreateLink( pDynamicLink->m_nSrcID, pDynamicLink->m_nDestID );
- if ( !pLink )
- DevMsg( "Failed to create dynamic link (%d <--> %d)\n", pDynamicLink->m_nSrcEditID, pDynamicLink->m_nDestEditID );
- }
-
- }
-
- if ( pLink )
- {
- bUpdateZones = true;
-
- int hullBits = ( pDynamicLink->GetSpawnFlags() & bits_HULL_BITS_MASK );
- for ( int i = 0; i < NUM_HULLS; i++ )
- {
- if ( hullBits & ( 1 << i ) )
- {
- pLink->m_iAcceptedMoveTypes[i] = pDynamicLink->m_nLinkType;
- }
- }
- }
- }
-
- // Now set the link's state
- pDynamicLink->SetLinkState();
-
- // Go on to the next dynamic link
- pDynamicLink = pDynamicLink->m_pNextDynamicLink;
- }
- else
- {
- CAI_DynamicLink *pBadDynamicLink = pDynamicLink;
-
- // Go on to the next dynamic link
- pDynamicLink = pDynamicLink->m_pNextDynamicLink;
-
- UTIL_RemoveImmediate( pBadDynamicLink );
- }
-
- }
-
- if ( bUpdateZones )
- {
- g_AINetworkBuilder.InitZones( g_pBigAINet );
- }
-}
-
-
-//------------------------------------------------------------------------------
-// Purpose : Goes through each dynamic link and updates the state of all
-// AINetwork links
-// Input :
-// Output :
-//------------------------------------------------------------------------------
-void CAI_DynamicLink::ResetDynamicLinks(void)
-{
- CAI_DynamicLink* pDynamicLink = CAI_DynamicLink::m_pAllDynamicLinks;
-
- while (pDynamicLink)
- {
- // Now set the link's state
- pDynamicLink->SetLinkState();
-
- // Go on to the next dynamic link
- pDynamicLink = pDynamicLink->m_pNextDynamicLink;
- }
-}
-
-
-//------------------------------------------------------------------------------
-// Purpose : Goes through each dynamic link and checks to make sure that
-// there is still a corresponding node link, if not removes it
-// Input :
-// Output :
-//------------------------------------------------------------------------------
-void CAI_DynamicLink::PurgeDynamicLinks(void)
-{
- CAI_DynamicLink* pDynamicLink = CAI_DynamicLink::m_pAllDynamicLinks;
-
- while (pDynamicLink)
- {
- if (!pDynamicLink->IsLinkValid())
- {
- // Didn't find the link, so remove it
-#ifdef _WIN32
- int nWCSrcID = g_pAINetworkManager->GetEditOps()->m_pNodeIndexTable[pDynamicLink->m_nSrcID];
- int nWCDstID = g_pAINetworkManager->GetEditOps()->m_pNodeIndexTable[pDynamicLink->m_nDestID];
- int status = Editor_DeleteNodeLink(nWCSrcID, nWCDstID, false);
- if (status == Editor_BadCommand)
- {
- DevMsg( "Worldcraft failed in PurgeDynamicLinks...\n" );
- }
-#endif
- // Safe to remove it here as this happens only after I leave this function
- UTIL_Remove(pDynamicLink);
- }
-
- // Go on to the next dynamic link
- pDynamicLink = pDynamicLink->m_pNextDynamicLink;
- }
-}
-
-//------------------------------------------------------------------------------
-// Purpose : Returns false if the dynamic link doesn't have a corresponding
-// node link
-// Input :
-// Output :
-//------------------------------------------------------------------------------
-bool CAI_DynamicLink::IsLinkValid( void )
-{
- CAI_Node *pNode = g_pBigAINet->GetNode(m_nSrcID);
-
- return ( pNode->GetLink( m_nDestID ) != NULL );
-}
-
-
-//------------------------------------------------------------------------------
-// Purpose :
-//------------------------------------------------------------------------------
-void CAI_DynamicLink::InputTurnOn( inputdata_t &inputdata )
-{
- if (m_nLinkState == LINK_OFF)
- {
- m_nLinkState = LINK_ON;
- CAI_DynamicLink::SetLinkState();
- }
-}
-
-
-//------------------------------------------------------------------------------
-// Purpose :
-//------------------------------------------------------------------------------
-void CAI_DynamicLink::InputTurnOff( inputdata_t &inputdata )
-{
- if (m_nLinkState == LINK_ON)
- {
- m_nLinkState = LINK_OFF;
- CAI_DynamicLink::SetLinkState();
- }
-}
-
-
-//------------------------------------------------------------------------------
-//------------------------------------------------------------------------------
-CAI_Link *CAI_DynamicLink::FindLink()
-{
- CAI_Node * pSrcNode = g_pBigAINet->GetNode(m_nSrcID, false);
- if ( pSrcNode )
- {
- int numLinks = pSrcNode->NumLinks();
- for (int i=0;i<numLinks;i++)
- {
- CAI_Link* pLink = pSrcNode->GetLinkByIndex(i);
-
- if (((pLink->m_iSrcID == m_nSrcID )&&
- (pLink->m_iDestID == m_nDestID)) ||
-
- ((pLink->m_iSrcID == m_nDestID)&&
- (pLink->m_iDestID == m_nSrcID )) )
- {
- return pLink;
- }
- }
- }
- return NULL;
-}
-
-//------------------------------------------------------------------------------
-//------------------------------------------------------------------------------
-int CAI_DynamicLink::ObjectCaps()
-{
- int caps = BaseClass::ObjectCaps();
-
- if ( m_bNotSaved )
- caps |= FCAP_DONT_SAVE;
-
- return caps;
-}
-
-//------------------------------------------------------------------------------
-// Purpose : Updates network link state if dynamic link state has changed
-// Input :
-// Output :
-//------------------------------------------------------------------------------
-void CAI_DynamicLink::SetLinkState(void)
-{
- if ( !gm_bInitialized )
- {
- // Safe to quietly return. Consistency will be enforced when InitDynamicLinks() is called
- return;
- }
-
- if (m_nSrcID == NO_NODE || m_nDestID == NO_NODE)
- {
- Vector pos = GetAbsOrigin();
- DevWarning("ERROR: Dynamic link at %f %f %f pointing to invalid node ID!!\n", pos.x, pos.y, pos.z);
- return;
- }
-
- // ------------------------------------------------------------------
- // Now update the node links...
- // Nodes share links so we only have to find the node from the src
- // For now just using one big AINetwork so find src node on that network
- // ------------------------------------------------------------------
- CAI_Node * pSrcNode = g_pBigAINet->GetNode(m_nSrcID, false);
- if ( pSrcNode )
- {
- CAI_Link* pLink = FindLink();
- if ( pLink )
- {
- pLink->m_pDynamicLink = this;
- if (m_nLinkState == LINK_OFF)
- {
- pLink->m_LinkInfo |= bits_LINK_OFF;
- }
- else
- {
- pLink->m_LinkInfo &= ~bits_LINK_OFF;
- }
- }
- else
- {
- DevMsg("Dynamic Link Error: (%s) unable to form between nodes %d and %d\n", GetDebugName(), m_nSrcID, m_nDestID );
- }
- }
-
-}
-
-//------------------------------------------------------------------------------
-// Purpose : Given two node ID's return the related dynamic link if any or NULL
-//
-// Input :
-// Output :
-//------------------------------------------------------------------------------
-CAI_DynamicLink* CAI_DynamicLink::GetDynamicLink(int nSrcID, int nDstID)
-{
- CAI_DynamicLink* pDynamicLink = CAI_DynamicLink::m_pAllDynamicLinks;
-
- while (pDynamicLink)
- {
- if ((nSrcID == pDynamicLink->m_nSrcID && nDstID == pDynamicLink->m_nDestID) ||
- (nSrcID == pDynamicLink->m_nDestID && nDstID == pDynamicLink->m_nSrcID ) )
- {
- return pDynamicLink;
- }
-
- // Go on to the next dynamic link
- pDynamicLink = pDynamicLink->m_pNextDynamicLink;
- }
- return NULL;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Constructor
-// Input :
-// Output :
-//-----------------------------------------------------------------------------
-CAI_DynamicLink::CAI_DynamicLink(void)
-{
- m_bFixedUpIds = false;
- m_bNotSaved = false;
- m_nSrcID = NO_NODE;
- m_nDestID = NO_NODE;
- m_nLinkState = LINK_OFF;
- m_nLinkType = bits_CAP_MOVE_GROUND;
- m_bInvertAllow = false;
-
- // -------------------------------------
- // Add to linked list of dynamic links
- // -------------------------------------
- m_pNextDynamicLink = CAI_DynamicLink::m_pAllDynamicLinks;
- CAI_DynamicLink::m_pAllDynamicLinks = this;
-};
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input :
-// Output :
-//-----------------------------------------------------------------------------
-CAI_DynamicLink::~CAI_DynamicLink(void) {
-
- // ----------------------------------------------
- // Remove from linked list of all dynamic links
- // ----------------------------------------------
- CAI_DynamicLink* pDynamicLink = CAI_DynamicLink::m_pAllDynamicLinks;
- if (pDynamicLink == this)
- {
- m_pAllDynamicLinks = pDynamicLink->m_pNextDynamicLink;
- }
- else
- {
- while (pDynamicLink)
- {
- if (pDynamicLink->m_pNextDynamicLink == this)
- {
- pDynamicLink->m_pNextDynamicLink = pDynamicLink->m_pNextDynamicLink->m_pNextDynamicLink;
- break;
- }
- pDynamicLink = pDynamicLink->m_pNextDynamicLink;
- }
- }
-}
-
-LINK_ENTITY_TO_CLASS(info_radial_link_controller, CAI_RadialLinkController);
-
-BEGIN_DATADESC( CAI_RadialLinkController )
-DEFINE_KEYFIELD( m_flRadius, FIELD_FLOAT, "radius" ),
-DEFINE_FIELD( m_vecAtRestOrigin, FIELD_POSITION_VECTOR ),
-DEFINE_FIELD( m_bAtRest, FIELD_BOOLEAN ),
-
-DEFINE_THINKFUNC( PollMotionThink ),
-END_DATADESC()
-
-//---------------------------------------------------------
-//---------------------------------------------------------
-void CAI_RadialLinkController::Spawn()
-{
- SetSolid( SOLID_NONE );
- AddEffects( EF_NODRAW );
-}
-
-//---------------------------------------------------------
-//---------------------------------------------------------
-void CAI_RadialLinkController::Activate()
-{
- BaseClass::Activate();
-
- m_bAtRest = false;
- m_vecAtRestOrigin = vec3_invalid;
-
- // Force re-evaluation
- SetThink( &CAI_RadialLinkController::PollMotionThink );
-
- // Spread think times out.
- SetNextThink( gpGlobals->curtime + random->RandomFloat( 0.0f, 1.0f) );
-
- if( GetParent() != NULL )
- {
- float flDist = GetAbsOrigin().DistTo( GetParent()->GetAbsOrigin() );
-
- if( flDist > 200.0f )
- {
- // Warn at the console if a link controller is far away from its parent. This
- // most likely means that a level designer has copied an entity without researching its hierarchy.
- DevMsg("RadialLinkController (%s) is far from its parent!\n", GetDebugName() );
- }
- }
-}
-
-//---------------------------------------------------------
-//---------------------------------------------------------
-void CAI_RadialLinkController::PollMotionThink()
-{
- SetNextThink( gpGlobals->curtime + 0.5f );
-
- CBaseEntity *pParent = GetParent();
-
- if( pParent )
- {
- if( pParent->VPhysicsGetObject()->IsAsleep() )
- {
- if( !m_bAtRest )
- {
- m_vecAtRestOrigin = GetAbsOrigin();
- ModifyNodeLinks( true );
- m_bAtRest = true;
- //Msg("At Rest!\n");
- }
- }
- else
- {
- if( m_bAtRest )
- {
- float flDist;
-
- flDist = GetAbsOrigin().DistTo(m_vecAtRestOrigin);
-
- if( flDist < 18.0f )
- {
- // Ignore movement If moved less than 18 inches from the place where we came to rest.
- //Msg("Reject.\n");
- return;
- }
- }
-
- //Msg("Polling!\n");
-
- if( m_vecAtRestOrigin != vec3_invalid )
- {
- ModifyNodeLinks( false );
- m_bAtRest = false;
- m_vecAtRestOrigin = vec3_invalid;
- }
- }
- }
-}
-
-//---------------------------------------------------------
-//---------------------------------------------------------
-ConVar ai_radial_max_link_dist( "ai_radial_max_link_dist", "512" );
-void CAI_RadialLinkController::ModifyNodeLinks( bool bMakeStale )
-{
- int nNodes = g_pBigAINet->NumNodes();
- CAI_Node **ppNodes = g_pBigAINet->AccessNodes();
-
- VPROF_BUDGET("ModifyLinks", "ModifyLinks");
-
- const float MinDistCareSq = Square( ai_radial_max_link_dist.GetFloat() + 0.1 );
-
- for ( int i = 0; i < nNodes; i++ )
- {
- CAI_Node *pNode = ppNodes[i];
- const Vector &nodeOrigin = pNode->GetOrigin();
- if ( m_vecAtRestOrigin.DistToSqr(nodeOrigin) < MinDistCareSq )
- {
- int nLinks = pNode->NumLinks();
- for ( int j = 0; j < nLinks; j++ )
- {
- CAI_Link *pLink = pNode->GetLinkByIndex( j );
- int iLinkDest = pLink->DestNodeID( i );
-
- if ( iLinkDest > i )
- {
- bool bQualify = true;
-
- if( ( (pLink->m_iAcceptedMoveTypes[HULL_HUMAN]||pLink->m_iAcceptedMoveTypes[HULL_WIDE_HUMAN]) & bits_CAP_MOVE_GROUND) == 0 )
- {
- // Micro-optimization: Ignore any connection that's not a walking connection for humans.(sjb)
- bQualify = false;
- }
-
- const Vector &originOther = ppNodes[iLinkDest]->GetOrigin();
- if ( bQualify && m_vecAtRestOrigin.DistToSqr(originOther) < MinDistCareSq )
- {
- if ( IsRayIntersectingSphere(nodeOrigin, originOther - nodeOrigin, m_vecAtRestOrigin, m_flRadius) )
- {
- if( bMakeStale )
- {
- pLink->m_LinkInfo |= bits_LINK_STALE_SUGGESTED;
- pLink->m_timeStaleExpires = FLT_MAX;
- }
- else
- {
- pLink->m_LinkInfo &= ~bits_LINK_STALE_SUGGESTED;
- }
- }
- }
- }
- }
- }
- }
-}
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: A link that can be turned on and off. Unlike normal links
+// dyanimc links must be entities so they can receive messages.
+// They update the state of the actual links. Allows us to save
+// a lot of memory by not making all links into entities
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "cbase.h"
+#include "collisionutils.h"
+#include "ai_dynamiclink.h"
+#include "ai_node.h"
+#include "ai_link.h"
+#include "ai_network.h"
+#include "ai_networkmanager.h"
+#include "saverestore_utlvector.h"
+#include "editor_sendcommand.h"
+#include "bitstring.h"
+#include "tier0/vprof.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+//-----------------------------------------------------------------------------
+
+LINK_ENTITY_TO_CLASS(info_node_link_controller, CAI_DynamicLinkController);
+
+BEGIN_DATADESC( CAI_DynamicLinkController )
+
+ DEFINE_KEYFIELD( m_nLinkState, FIELD_INTEGER, "initialstate" ),
+ DEFINE_KEYFIELD( m_strAllowUse, FIELD_STRING, "AllowUse" ),
+ DEFINE_KEYFIELD( m_bInvertAllow, FIELD_BOOLEAN, "InvertAllow" ),
+ DEFINE_KEYFIELD( m_bUseAirLinkRadius, FIELD_BOOLEAN, "useairlinkradius" ),
+ // m_ControlledLinks (rebuilt)
+
+ DEFINE_INPUTFUNC( FIELD_VOID, "TurnOn", InputTurnOn ),
+ DEFINE_INPUTFUNC( FIELD_VOID, "TurnOff", InputTurnOff ),
+
+ DEFINE_INPUTFUNC( FIELD_STRING, "SetAllowed", InputSetAllowed ),
+ DEFINE_INPUTFUNC( FIELD_BOOLEAN, "SetInvert", InputSetInvert ),
+
+END_DATADESC()
+
+void CAI_DynamicLinkController::GenerateLinksFromVolume()
+{
+ Assert( m_ControlledLinks.Count() == 0 );
+
+ int nNodes = g_pBigAINet->NumNodes();
+ CAI_Node **ppNodes = g_pBigAINet->AccessNodes();
+
+ float MinDistCareSq = 0;
+ if (m_bUseAirLinkRadius)
+ {
+ MinDistCareSq = Square(MAX_AIR_NODE_LINK_DIST + 0.1);
+ }
+ else
+ {
+ MinDistCareSq = Square(MAX_NODE_LINK_DIST + 0.1);
+ }
+
+ const Vector &origin = WorldSpaceCenter();
+ Vector vAbsMins, vAbsMaxs;
+ CollisionProp()->WorldSpaceAABB( &vAbsMins, &vAbsMaxs );
+ vAbsMins -= Vector( 1, 1, 1 );
+ vAbsMaxs += Vector( 1, 1, 1 );
+
+ for ( int i = 0; i < nNodes; i++ )
+ {
+ CAI_Node *pNode = ppNodes[i];
+ const Vector &nodeOrigin = pNode->GetOrigin();
+ if ( origin.DistToSqr(nodeOrigin) < MinDistCareSq )
+ {
+ int nLinks = pNode->NumLinks();
+ for ( int j = 0; j < nLinks; j++ )
+ {
+ CAI_Link *pLink = pNode->GetLinkByIndex( j );
+ int iLinkDest = pLink->DestNodeID( i );
+ if ( iLinkDest > i )
+ {
+ const Vector &originOther = ppNodes[iLinkDest]->GetOrigin();
+ if ( origin.DistToSqr(originOther) < MinDistCareSq )
+ {
+ if ( IsBoxIntersectingRay( vAbsMins, vAbsMaxs, nodeOrigin, originOther - nodeOrigin ) )
+ {
+ Assert( IsBoxIntersectingRay( vAbsMins, vAbsMaxs, originOther, nodeOrigin - originOther ) );
+
+ CAI_DynamicLink *pLink = (CAI_DynamicLink *)CreateEntityByName( "info_node_link" );
+ pLink->m_nSrcID = i;
+ pLink->m_nDestID = iLinkDest;
+ pLink->m_nSrcEditID = g_pAINetworkManager->GetEditOps()->GetWCIdFromNodeId( pLink->m_nSrcID );
+ pLink->m_nDestEditID = g_pAINetworkManager->GetEditOps()->GetWCIdFromNodeId( pLink->m_nDestID );
+ pLink->m_nLinkState = m_nLinkState;
+ pLink->m_strAllowUse = m_strAllowUse;
+ pLink->m_bInvertAllow = m_bInvertAllow;
+ pLink->m_bFixedUpIds = true;
+ pLink->m_bNotSaved = true;
+
+ pLink->Spawn();
+ m_ControlledLinks.AddToTail( pLink );
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+void CAI_DynamicLinkController::InputTurnOn( inputdata_t &inputdata )
+{
+ for ( int i = 0; i < m_ControlledLinks.Count(); i++ )
+ {
+ if ( m_ControlledLinks[i] == NULL )
+ {
+ m_ControlledLinks.FastRemove(i);
+ if ( i >= m_ControlledLinks.Count() )
+ break;
+ }
+ m_ControlledLinks[i]->InputTurnOn( inputdata );
+ }
+
+ m_nLinkState = LINK_ON;
+}
+
+void CAI_DynamicLinkController::InputTurnOff( inputdata_t &inputdata )
+{
+ for ( int i = 0; i < m_ControlledLinks.Count(); i++ )
+ {
+ if ( m_ControlledLinks[i] == NULL )
+ {
+ m_ControlledLinks.FastRemove(i);
+ if ( i >= m_ControlledLinks.Count() )
+ break;
+ }
+ m_ControlledLinks[i]->InputTurnOff( inputdata );
+ }
+
+ m_nLinkState = LINK_OFF;
+}
+
+void CAI_DynamicLinkController::InputSetAllowed( inputdata_t &inputdata )
+{
+ m_strAllowUse = inputdata.value.StringID();
+ for ( int i = 0; i < m_ControlledLinks.Count(); i++ )
+ {
+ if ( m_ControlledLinks[i] == NULL )
+ {
+ m_ControlledLinks.FastRemove(i);
+ if ( i >= m_ControlledLinks.Count() )
+ break;
+ }
+ m_ControlledLinks[i]->m_strAllowUse = m_strAllowUse;
+ }
+}
+
+void CAI_DynamicLinkController::InputSetInvert( inputdata_t &inputdata )
+{
+ m_bInvertAllow = inputdata.value.Bool();
+ for ( int i = 0; i < m_ControlledLinks.Count(); i++ )
+ {
+ if ( m_ControlledLinks[i] == NULL )
+ {
+ m_ControlledLinks.FastRemove(i);
+ if ( i >= m_ControlledLinks.Count() )
+ break;
+ }
+ m_ControlledLinks[i]->m_bInvertAllow = m_bInvertAllow;
+ }
+}
+
+//-----------------------------------------------------------------------------
+
+LINK_ENTITY_TO_CLASS(info_node_link, CAI_DynamicLink);
+
+BEGIN_DATADESC( CAI_DynamicLink )
+
+// m_pNextDynamicLink
+DEFINE_KEYFIELD( m_nLinkState, FIELD_INTEGER, "initialstate" ),
+DEFINE_KEYFIELD( m_nSrcEditID, FIELD_INTEGER, "startnode" ),
+DEFINE_KEYFIELD( m_nDestEditID, FIELD_INTEGER, "endnode" ),
+DEFINE_KEYFIELD( m_nLinkType, FIELD_INTEGER, "linktype" ),
+DEFINE_FIELD( m_bInvertAllow, FIELD_BOOLEAN ),
+// m_nSrcID (rebuilt)
+// m_nDestID (rebuilt)
+DEFINE_KEYFIELD( m_strAllowUse, FIELD_STRING, "AllowUse" ),
+// m_bFixedUpIds (part of rebuild)
+// m_bNotSaved (rebuilt)
+
+DEFINE_INPUTFUNC( FIELD_VOID, "TurnOn", InputTurnOn ),
+DEFINE_INPUTFUNC( FIELD_VOID, "TurnOff", InputTurnOff ),
+
+END_DATADESC()
+
+//-----------------------------------------------------------------------------
+// Init static variables
+//-----------------------------------------------------------------------------
+CAI_DynamicLink *CAI_DynamicLink::m_pAllDynamicLinks = NULL;
+bool CAI_DynamicLink::gm_bInitialized;
+
+
+//------------------------------------------------------------------------------
+
+void CAI_DynamicLink::GenerateControllerLinks()
+{
+ CAI_DynamicLinkController *pController = NULL;
+ while ( ( pController = gEntList.NextEntByClass( pController ) ) != NULL )
+ {
+ pController->GenerateLinksFromVolume();
+ }
+
+}
+
+//------------------------------------------------------------------------------
+// Purpose : Initializes src and dest IDs for all dynamic links
+//
+// Input :
+// Output :
+//------------------------------------------------------------------------------
+void CAI_DynamicLink::InitDynamicLinks(void)
+{
+ if (!g_pAINetworkManager->GetEditOps()->m_pNodeIndexTable)
+ {
+ Warning("ERROR: Trying initialize links with no WC ID table!\n");
+ return;
+ }
+
+ if ( gm_bInitialized )
+ return;
+
+ gm_bInitialized = true;
+
+ bool bUpdateZones = false;
+
+ GenerateControllerLinks();
+
+ CAI_DynamicLink* pDynamicLink = CAI_DynamicLink::m_pAllDynamicLinks;
+
+ while (pDynamicLink)
+ {
+ // -------------------------------------------------------------
+ // First convert this links WC IDs to engine IDs
+ // -------------------------------------------------------------
+ if ( !pDynamicLink->m_bFixedUpIds )
+ {
+ int nSrcID = g_pAINetworkManager->GetEditOps()->GetNodeIdFromWCId( pDynamicLink->m_nSrcEditID );
+ if (nSrcID == -1)
+ {
+ DevMsg( "ERROR: Dynamic link source WC node %d not found\n", pDynamicLink->m_nSrcEditID );
+ nSrcID = NO_NODE;
+ }
+
+ int nDestID = g_pAINetworkManager->GetEditOps()->GetNodeIdFromWCId( pDynamicLink->m_nDestEditID );
+ if (nDestID == -1)
+ {
+ DevMsg( "ERROR: Dynamic link dest WC node %d not found\n", pDynamicLink->m_nDestEditID );
+ nDestID = NO_NODE;
+ }
+
+ pDynamicLink->m_nSrcID = nSrcID;
+ pDynamicLink->m_nDestID = nDestID;
+ pDynamicLink->m_bFixedUpIds = true;
+ }
+
+ if ( pDynamicLink->m_nSrcID != NO_NODE && pDynamicLink->m_nDestID != NO_NODE )
+ {
+ if ( ( pDynamicLink->GetSpawnFlags() & bits_HULL_BITS_MASK ) != 0 )
+ {
+ CAI_Link *pLink = pDynamicLink->FindLink();
+ if ( !pLink )
+ {
+ CAI_Node *pNode1, *pNode2;
+
+ pNode1 = g_pBigAINet->GetNode( pDynamicLink->m_nSrcID );
+ pNode2 = g_pBigAINet->GetNode( pDynamicLink->m_nDestID );
+
+ if ( pNode1 && pNode2 )
+ {
+ pLink = g_pBigAINet->CreateLink( pDynamicLink->m_nSrcID, pDynamicLink->m_nDestID );
+ if ( !pLink )
+ DevMsg( "Failed to create dynamic link (%d <--> %d)\n", pDynamicLink->m_nSrcEditID, pDynamicLink->m_nDestEditID );
+ }
+
+ }
+
+ if ( pLink )
+ {
+ bUpdateZones = true;
+
+ int hullBits = ( pDynamicLink->GetSpawnFlags() & bits_HULL_BITS_MASK );
+ for ( int i = 0; i < NUM_HULLS; i++ )
+ {
+ if ( hullBits & ( 1 << i ) )
+ {
+ pLink->m_iAcceptedMoveTypes[i] = pDynamicLink->m_nLinkType;
+ }
+ }
+ }
+ }
+
+ // Now set the link's state
+ pDynamicLink->SetLinkState();
+
+ // Go on to the next dynamic link
+ pDynamicLink = pDynamicLink->m_pNextDynamicLink;
+ }
+ else
+ {
+ CAI_DynamicLink *pBadDynamicLink = pDynamicLink;
+
+ // Go on to the next dynamic link
+ pDynamicLink = pDynamicLink->m_pNextDynamicLink;
+
+ UTIL_RemoveImmediate( pBadDynamicLink );
+ }
+
+ }
+
+ if ( bUpdateZones )
+ {
+ g_AINetworkBuilder.InitZones( g_pBigAINet );
+ }
+}
+
+
+//------------------------------------------------------------------------------
+// Purpose : Goes through each dynamic link and updates the state of all
+// AINetwork links
+// Input :
+// Output :
+//------------------------------------------------------------------------------
+void CAI_DynamicLink::ResetDynamicLinks(void)
+{
+ CAI_DynamicLink* pDynamicLink = CAI_DynamicLink::m_pAllDynamicLinks;
+
+ while (pDynamicLink)
+ {
+ // Now set the link's state
+ pDynamicLink->SetLinkState();
+
+ // Go on to the next dynamic link
+ pDynamicLink = pDynamicLink->m_pNextDynamicLink;
+ }
+}
+
+
+//------------------------------------------------------------------------------
+// Purpose : Goes through each dynamic link and checks to make sure that
+// there is still a corresponding node link, if not removes it
+// Input :
+// Output :
+//------------------------------------------------------------------------------
+void CAI_DynamicLink::PurgeDynamicLinks(void)
+{
+ CAI_DynamicLink* pDynamicLink = CAI_DynamicLink::m_pAllDynamicLinks;
+
+ while (pDynamicLink)
+ {
+ if (!pDynamicLink->IsLinkValid())
+ {
+ // Didn't find the link, so remove it
+#ifdef _WIN32
+ int nWCSrcID = g_pAINetworkManager->GetEditOps()->m_pNodeIndexTable[pDynamicLink->m_nSrcID];
+ int nWCDstID = g_pAINetworkManager->GetEditOps()->m_pNodeIndexTable[pDynamicLink->m_nDestID];
+ int status = Editor_DeleteNodeLink(nWCSrcID, nWCDstID, false);
+ if (status == Editor_BadCommand)
+ {
+ DevMsg( "Worldcraft failed in PurgeDynamicLinks...\n" );
+ }
+#endif
+ // Safe to remove it here as this happens only after I leave this function
+ UTIL_Remove(pDynamicLink);
+ }
+
+ // Go on to the next dynamic link
+ pDynamicLink = pDynamicLink->m_pNextDynamicLink;
+ }
+}
+
+//------------------------------------------------------------------------------
+// Purpose : Returns false if the dynamic link doesn't have a corresponding
+// node link
+// Input :
+// Output :
+//------------------------------------------------------------------------------
+bool CAI_DynamicLink::IsLinkValid( void )
+{
+ CAI_Node *pNode = g_pBigAINet->GetNode(m_nSrcID);
+
+ return ( pNode->GetLink( m_nDestID ) != NULL );
+}
+
+
+//------------------------------------------------------------------------------
+// Purpose :
+//------------------------------------------------------------------------------
+void CAI_DynamicLink::InputTurnOn( inputdata_t &inputdata )
+{
+ if (m_nLinkState == LINK_OFF)
+ {
+ m_nLinkState = LINK_ON;
+ CAI_DynamicLink::SetLinkState();
+ }
+}
+
+
+//------------------------------------------------------------------------------
+// Purpose :
+//------------------------------------------------------------------------------
+void CAI_DynamicLink::InputTurnOff( inputdata_t &inputdata )
+{
+ if (m_nLinkState == LINK_ON)
+ {
+ m_nLinkState = LINK_OFF;
+ CAI_DynamicLink::SetLinkState();
+ }
+}
+
+
+//------------------------------------------------------------------------------
+//------------------------------------------------------------------------------
+CAI_Link *CAI_DynamicLink::FindLink()
+{
+ CAI_Node * pSrcNode = g_pBigAINet->GetNode(m_nSrcID, false);
+ if ( pSrcNode )
+ {
+ int numLinks = pSrcNode->NumLinks();
+ for (int i=0;i<numLinks;i++)
+ {
+ CAI_Link* pLink = pSrcNode->GetLinkByIndex(i);
+
+ if (((pLink->m_iSrcID == m_nSrcID )&&
+ (pLink->m_iDestID == m_nDestID)) ||
+
+ ((pLink->m_iSrcID == m_nDestID)&&
+ (pLink->m_iDestID == m_nSrcID )) )
+ {
+ return pLink;
+ }
+ }
+ }
+ return NULL;
+}
+
+//------------------------------------------------------------------------------
+//------------------------------------------------------------------------------
+int CAI_DynamicLink::ObjectCaps()
+{
+ int caps = BaseClass::ObjectCaps();
+
+ if ( m_bNotSaved )
+ caps |= FCAP_DONT_SAVE;
+
+ return caps;
+}
+
+//------------------------------------------------------------------------------
+// Purpose : Updates network link state if dynamic link state has changed
+// Input :
+// Output :
+//------------------------------------------------------------------------------
+void CAI_DynamicLink::SetLinkState(void)
+{
+ if ( !gm_bInitialized )
+ {
+ // Safe to quietly return. Consistency will be enforced when InitDynamicLinks() is called
+ return;
+ }
+
+ if (m_nSrcID == NO_NODE || m_nDestID == NO_NODE)
+ {
+ Vector pos = GetAbsOrigin();
+ DevWarning("ERROR: Dynamic link at %f %f %f pointing to invalid node ID!!\n", pos.x, pos.y, pos.z);
+ return;
+ }
+
+ // ------------------------------------------------------------------
+ // Now update the node links...
+ // Nodes share links so we only have to find the node from the src
+ // For now just using one big AINetwork so find src node on that network
+ // ------------------------------------------------------------------
+ CAI_Node * pSrcNode = g_pBigAINet->GetNode(m_nSrcID, false);
+ if ( pSrcNode )
+ {
+ CAI_Link* pLink = FindLink();
+ if ( pLink )
+ {
+ pLink->m_pDynamicLink = this;
+ if (m_nLinkState == LINK_OFF)
+ {
+ pLink->m_LinkInfo |= bits_LINK_OFF;
+ }
+ else
+ {
+ pLink->m_LinkInfo &= ~bits_LINK_OFF;
+ }
+ }
+ else
+ {
+ DevMsg("Dynamic Link Error: (%s) unable to form between nodes %d and %d\n", GetDebugName(), m_nSrcID, m_nDestID );
+ }
+ }
+
+}
+
+//------------------------------------------------------------------------------
+// Purpose : Given two node ID's return the related dynamic link if any or NULL
+//
+// Input :
+// Output :
+//------------------------------------------------------------------------------
+CAI_DynamicLink* CAI_DynamicLink::GetDynamicLink(int nSrcID, int nDstID)
+{
+ CAI_DynamicLink* pDynamicLink = CAI_DynamicLink::m_pAllDynamicLinks;
+
+ while (pDynamicLink)
+ {
+ if ((nSrcID == pDynamicLink->m_nSrcID && nDstID == pDynamicLink->m_nDestID) ||
+ (nSrcID == pDynamicLink->m_nDestID && nDstID == pDynamicLink->m_nSrcID ) )
+ {
+ return pDynamicLink;
+ }
+
+ // Go on to the next dynamic link
+ pDynamicLink = pDynamicLink->m_pNextDynamicLink;
+ }
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+// Input :
+// Output :
+//-----------------------------------------------------------------------------
+CAI_DynamicLink::CAI_DynamicLink(void)
+{
+ m_bFixedUpIds = false;
+ m_bNotSaved = false;
+ m_nSrcID = NO_NODE;
+ m_nDestID = NO_NODE;
+ m_nLinkState = LINK_OFF;
+ m_nLinkType = bits_CAP_MOVE_GROUND;
+ m_bInvertAllow = false;
+
+ // -------------------------------------
+ // Add to linked list of dynamic links
+ // -------------------------------------
+ m_pNextDynamicLink = CAI_DynamicLink::m_pAllDynamicLinks;
+ CAI_DynamicLink::m_pAllDynamicLinks = this;
+};
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input :
+// Output :
+//-----------------------------------------------------------------------------
+CAI_DynamicLink::~CAI_DynamicLink(void) {
+
+ // ----------------------------------------------
+ // Remove from linked list of all dynamic links
+ // ----------------------------------------------
+ CAI_DynamicLink* pDynamicLink = CAI_DynamicLink::m_pAllDynamicLinks;
+ if (pDynamicLink == this)
+ {
+ m_pAllDynamicLinks = pDynamicLink->m_pNextDynamicLink;
+ }
+ else
+ {
+ while (pDynamicLink)
+ {
+ if (pDynamicLink->m_pNextDynamicLink == this)
+ {
+ pDynamicLink->m_pNextDynamicLink = pDynamicLink->m_pNextDynamicLink->m_pNextDynamicLink;
+ break;
+ }
+ pDynamicLink = pDynamicLink->m_pNextDynamicLink;
+ }
+ }
+}
+
+LINK_ENTITY_TO_CLASS(info_radial_link_controller, CAI_RadialLinkController);
+
+BEGIN_DATADESC( CAI_RadialLinkController )
+DEFINE_KEYFIELD( m_flRadius, FIELD_FLOAT, "radius" ),
+DEFINE_FIELD( m_vecAtRestOrigin, FIELD_POSITION_VECTOR ),
+DEFINE_FIELD( m_bAtRest, FIELD_BOOLEAN ),
+
+DEFINE_THINKFUNC( PollMotionThink ),
+END_DATADESC()
+
+//---------------------------------------------------------
+//---------------------------------------------------------
+void CAI_RadialLinkController::Spawn()
+{
+ SetSolid( SOLID_NONE );
+ AddEffects( EF_NODRAW );
+}
+
+//---------------------------------------------------------
+//---------------------------------------------------------
+void CAI_RadialLinkController::Activate()
+{
+ BaseClass::Activate();
+
+ m_bAtRest = false;
+ m_vecAtRestOrigin = vec3_invalid;
+
+ // Force re-evaluation
+ SetThink( &CAI_RadialLinkController::PollMotionThink );
+
+ // Spread think times out.
+ SetNextThink( gpGlobals->curtime + random->RandomFloat( 0.0f, 1.0f) );
+
+ if( GetParent() != NULL )
+ {
+ float flDist = GetAbsOrigin().DistTo( GetParent()->GetAbsOrigin() );
+
+ if( flDist > 200.0f )
+ {
+ // Warn at the console if a link controller is far away from its parent. This
+ // most likely means that a level designer has copied an entity without researching its hierarchy.
+ DevMsg("RadialLinkController (%s) is far from its parent!\n", GetDebugName() );
+ }
+ }
+}
+
+//---------------------------------------------------------
+//---------------------------------------------------------
+void CAI_RadialLinkController::PollMotionThink()
+{
+ SetNextThink( gpGlobals->curtime + 0.5f );
+
+ CBaseEntity *pParent = GetParent();
+
+ if( pParent )
+ {
+ if( pParent->VPhysicsGetObject()->IsAsleep() )
+ {
+ if( !m_bAtRest )
+ {
+ m_vecAtRestOrigin = GetAbsOrigin();
+ ModifyNodeLinks( true );
+ m_bAtRest = true;
+ //Msg("At Rest!\n");
+ }
+ }
+ else
+ {
+ if( m_bAtRest )
+ {
+ float flDist;
+
+ flDist = GetAbsOrigin().DistTo(m_vecAtRestOrigin);
+
+ if( flDist < 18.0f )
+ {
+ // Ignore movement If moved less than 18 inches from the place where we came to rest.
+ //Msg("Reject.\n");
+ return;
+ }
+ }
+
+ //Msg("Polling!\n");
+
+ if( m_vecAtRestOrigin != vec3_invalid )
+ {
+ ModifyNodeLinks( false );
+ m_bAtRest = false;
+ m_vecAtRestOrigin = vec3_invalid;
+ }
+ }
+ }
+}
+
+//---------------------------------------------------------
+//---------------------------------------------------------
+ConVar ai_radial_max_link_dist( "ai_radial_max_link_dist", "512" );
+void CAI_RadialLinkController::ModifyNodeLinks( bool bMakeStale )
+{
+ int nNodes = g_pBigAINet->NumNodes();
+ CAI_Node **ppNodes = g_pBigAINet->AccessNodes();
+
+ VPROF_BUDGET("ModifyLinks", "ModifyLinks");
+
+ const float MinDistCareSq = Square( ai_radial_max_link_dist.GetFloat() + 0.1 );
+
+ for ( int i = 0; i < nNodes; i++ )
+ {
+ CAI_Node *pNode = ppNodes[i];
+ const Vector &nodeOrigin = pNode->GetOrigin();
+ if ( m_vecAtRestOrigin.DistToSqr(nodeOrigin) < MinDistCareSq )
+ {
+ int nLinks = pNode->NumLinks();
+ for ( int j = 0; j < nLinks; j++ )
+ {
+ CAI_Link *pLink = pNode->GetLinkByIndex( j );
+ int iLinkDest = pLink->DestNodeID( i );
+
+ if ( iLinkDest > i )
+ {
+ bool bQualify = true;
+
+ if( ( (pLink->m_iAcceptedMoveTypes[HULL_HUMAN]||pLink->m_iAcceptedMoveTypes[HULL_WIDE_HUMAN]) & bits_CAP_MOVE_GROUND) == 0 )
+ {
+ // Micro-optimization: Ignore any connection that's not a walking connection for humans.(sjb)
+ bQualify = false;
+ }
+
+ const Vector &originOther = ppNodes[iLinkDest]->GetOrigin();
+ if ( bQualify && m_vecAtRestOrigin.DistToSqr(originOther) < MinDistCareSq )
+ {
+ if ( IsRayIntersectingSphere(nodeOrigin, originOther - nodeOrigin, m_vecAtRestOrigin, m_flRadius) )
+ {
+ if( bMakeStale )
+ {
+ pLink->m_LinkInfo |= bits_LINK_STALE_SUGGESTED;
+ pLink->m_timeStaleExpires = FLT_MAX;
+ }
+ else
+ {
+ pLink->m_LinkInfo &= ~bits_LINK_STALE_SUGGESTED;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}