Counter Strike : Global Offensive Source Code
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.

3404 lines
103 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "fmtstr.h"
  8. #include "filesystem.h"
  9. #include "filesystem/IQueuedLoader.h"
  10. #include "utlbuffer.h"
  11. #include "utlrbtree.h"
  12. #include "editor_sendcommand.h"
  13. #include "ai_networkmanager.h"
  14. #include "ai_network.h"
  15. #include "ai_node.h"
  16. #include "ai_navigator.h"
  17. #include "ai_link.h"
  18. #include "ai_dynamiclink.h"
  19. #include "ai_initutils.h"
  20. #include "ai_moveprobe.h"
  21. #include "ai_hull.h"
  22. #include "ndebugoverlay.h"
  23. #include "ai_hint.h"
  24. // memdbgon must be the last include file in a .cpp file!!!
  25. #include "tier0/memdbgon.h"
  26. // Increment this to force rebuilding of all networks
  27. #define AINET_VERSION_NUMBER 41
  28. //-----------------------------------------------------------------------------
  29. int g_DebugConnectNode1 = -1;
  30. int g_DebugConnectNode2 = -1;
  31. #define DebuggingConnect( node1, node2 ) ( ( node1 == g_DebugConnectNode1 && node2 == g_DebugConnectNode2 ) || ( node1 == g_DebugConnectNode2 && node2 == g_DebugConnectNode1 ) )
  32. inline void DebugConnectMsg( int node1, int node2, const char *pszFormat, ... )
  33. {
  34. if ( DebuggingConnect( node1, node2 ) )
  35. {
  36. DevMsg( "%s", CFmtStr( &pszFormat ).String() );
  37. }
  38. }
  39. CON_COMMAND( ai_debug_node_connect, "Debug the attempted connection between two nodes" )
  40. {
  41. g_DebugConnectNode1 = atoi( args[1] );
  42. g_DebugConnectNode2 = atoi( args[2] );
  43. DevMsg( "ai_debug_node_connect: debugging enbabled for %d <--> %d\n", g_DebugConnectNode1, g_DebugConnectNode2 );
  44. }
  45. //-----------------------------------------------------------------------------
  46. // This CVAR allows level designers to override the building
  47. // of node graphs due to date conflicts with the BSP and AIN
  48. // files. That way they don't have to wait for the node graph
  49. // to rebuild following small only-ents changes. This CVAR
  50. // always defaults to 0 and must be set at the command
  51. // line to properly override the node graph building.
  52. ConVar g_ai_norebuildgraph( "ai_norebuildgraph", "0" );
  53. ConVar g_ai_threadedgraphbuild( "g_ai_threadedgraphbuild", "0", FCVAR_NONE, "If true, use experimental threaded node graph building." );
  54. //-----------------------------------------------------------------------------
  55. // CAI_NetworkManager
  56. //
  57. //-----------------------------------------------------------------------------
  58. CAI_NetworkManager *g_pAINetworkManager;
  59. //-----------------------------------------------------------------------------
  60. bool CAI_NetworkManager::gm_fNetworksLoaded;
  61. LINK_ENTITY_TO_CLASS(ai_network,CAI_NetworkManager);
  62. BEGIN_DATADESC( CAI_NetworkManager )
  63. DEFINE_FIELD( m_bNeedGraphRebuild, FIELD_BOOLEAN ),
  64. // m_pEditOps
  65. // m_pNetwork
  66. // DEFINE_FIELD( m_bDontSaveGraph, FIELD_BOOLEAN ),
  67. DEFINE_FIELD( m_fInitalized, FIELD_BOOLEAN ),
  68. // Function pointers
  69. DEFINE_FUNCTION( DelayedInit ),
  70. DEFINE_FUNCTION( ThreadedInit ),
  71. DEFINE_FUNCTION( RebuildThink ),
  72. END_DATADESC()
  73. //-----------------------------------------------------------------------------
  74. CAI_NetworkManager::CAI_NetworkManager(void)
  75. {
  76. m_pNetwork = new CAI_Network;
  77. m_pEditOps = new CAI_NetworkEditTools(this);
  78. m_bNeedGraphRebuild = false;
  79. m_fInitalized = false;
  80. CAI_DynamicLink::gm_bInitialized = false;
  81. // ---------------------------------
  82. // Add to linked list of networks
  83. // ---------------------------------
  84. };
  85. //-----------------------------------------------------------------------------
  86. CAI_NetworkManager::~CAI_NetworkManager(void)
  87. {
  88. // ---------------------------------------
  89. // Remove from linked list of AINetworks
  90. // ---------------------------------------
  91. delete m_pEditOps;
  92. delete m_pNetwork;
  93. if ( g_pAINetworkManager == this )
  94. {
  95. g_pAINetworkManager = NULL;
  96. }
  97. }
  98. //------------------------------------------------------------------------------
  99. // Purpose : Think function so we can put message on screen saying we are
  100. // going to rebuild the network, before we hang during the rebuild
  101. //------------------------------------------------------------------------------
  102. void CAI_NetworkManager::RebuildThink( void )
  103. {
  104. SetThink(NULL);
  105. GetEditOps()->m_debugNetOverlays &= ~bits_debugNeedRebuild;
  106. StartRebuild( );
  107. }
  108. //------------------------------------------------------------------------------
  109. // Purpose : Delay function so we can put message on screen saying we are
  110. // going to rebuild the network, before we hang during the rebuild
  111. //------------------------------------------------------------------------------
  112. void CAI_NetworkManager::RebuildNetworkGraph( void )
  113. {
  114. if (m_pfnThink != (void (CBaseEntity::*)())&CAI_NetworkManager::RebuildThink)
  115. {
  116. UTIL_CenterPrintAll( "Doing partial rebuild of Node Graph...\n" );
  117. SetThink(&CAI_NetworkManager::RebuildThink);
  118. SetNextThink( gpGlobals->curtime + 0.1f );
  119. }
  120. }
  121. //-----------------------------------------------------------------------------
  122. // Purpose: Used for WC edit move to rebuild the network around the given
  123. // location. Rebuilding the entire network takes too long
  124. //-----------------------------------------------------------------------------
  125. void CAI_NetworkManager::StartRebuild( void )
  126. {
  127. CAI_DynamicLink::gm_bInitialized = false;
  128. g_AINetworkBuilder.Rebuild( m_pNetwork );
  129. // ------------------------------------------------------------
  130. // Purge any dynamic links for links that don't exist any more
  131. // ------------------------------------------------------------
  132. CAI_DynamicLink::PurgeDynamicLinks();
  133. // ------------------------
  134. // Reset all dynamic links
  135. // ------------------------
  136. CAI_DynamicLink::ResetDynamicLinks();
  137. // --------------------------------------------------
  138. // Update display of usable nodes for displayed hull
  139. // --------------------------------------------------
  140. GetEditOps()->RecalcUsableNodesForHull();
  141. GetEditOps()->ClearRebuildFlags();
  142. }
  143. //-----------------------------------------------------------------------------
  144. // Purpose: Called by save restore code if no valid load graph was loaded at restore time.
  145. // Prevents writing out of a "bogus" node graph...
  146. // Input : -
  147. //-----------------------------------------------------------------------------
  148. void CAI_NetworkManager::MarkDontSaveGraph()
  149. {
  150. m_bDontSaveGraph = true;
  151. }
  152. //-----------------------------------------------------------------------------
  153. // Purpose: Only called if network has changed since last time level
  154. // was loaded
  155. // Input :
  156. // Output :
  157. //-----------------------------------------------------------------------------
  158. void CAI_NetworkManager::SaveNetworkGraph( void )
  159. {
  160. #if defined( PORTAL2 )
  161. // not used
  162. return;
  163. #endif
  164. if ( m_bDontSaveGraph )
  165. return;
  166. if ( !m_bNeedGraphRebuild )
  167. return;
  168. //if ( g_AI_Manager.NumAIs() && m_pNetwork->m_iNumNodes == 0 )
  169. //{
  170. // return;
  171. //}
  172. if ( !g_pGameRules->FAllowNPCs() )
  173. {
  174. return;
  175. }
  176. // -----------------------------
  177. // Make sure directories have been made
  178. // -----------------------------
  179. char szNrpFilename [MAX_PATH];// text node report filename
  180. Q_strncpy( szNrpFilename, "maps/graphs" ,sizeof(szNrpFilename));
  181. // Usually adding on the map filename and stripping it does nothing, but if the map is under a subdir,
  182. // this makes it create the correct subdir under maps/graphs.
  183. char tempFilename[MAX_PATH];
  184. Q_snprintf( tempFilename, sizeof( tempFilename ), "%s/%s", szNrpFilename, STRING( gpGlobals->mapname ) );
  185. // Remove the filename.
  186. int len = strlen( tempFilename );
  187. for ( int i=0; i < len; i++ )
  188. {
  189. if ( tempFilename[len-i-1] == '/' || tempFilename[len-i-1] == '\\' )
  190. {
  191. tempFilename[len-i-1] = 0;
  192. break;
  193. }
  194. }
  195. // Make sure the directories we need exist.
  196. filesystem->CreateDirHierarchy( tempFilename, "DEFAULT_WRITE_PATH" );
  197. // Now add the real map filename.
  198. Q_strncat( szNrpFilename, "/", sizeof( szNrpFilename ), COPY_ALL_CHARACTERS );
  199. Q_strncat( szNrpFilename, STRING( gpGlobals->mapname ), sizeof( szNrpFilename ), COPY_ALL_CHARACTERS );
  200. Q_strncat( szNrpFilename, PLATFORM_EXT ".ain", sizeof( szNrpFilename ), COPY_ALL_CHARACTERS );
  201. CUtlBuffer buf;
  202. // ---------------------------
  203. // Save the version number
  204. // ---------------------------
  205. buf.PutInt(AINET_VERSION_NUMBER);
  206. buf.PutInt(gpGlobals->mapversion);
  207. // -------------------------------
  208. // Dump all the nodes to the file
  209. // -------------------------------
  210. buf.PutInt( m_pNetwork->m_iNumNodes);
  211. int node;
  212. int totalNumLinks = 0;
  213. for ( node = 0; node < m_pNetwork->m_iNumNodes; node++)
  214. {
  215. CAI_Node *pNode = m_pNetwork->GetNode(node);
  216. Assert( pNode->GetZone() != AI_NODE_ZONE_UNKNOWN );
  217. buf.PutFloat( pNode->GetOrigin().x );
  218. buf.PutFloat( pNode->GetOrigin().y );
  219. buf.PutFloat( pNode->GetOrigin().z );
  220. buf.PutFloat( pNode->GetYaw() );
  221. buf.Put( pNode->m_flVOffset, sizeof( pNode->m_flVOffset ) );
  222. buf.PutChar( pNode->GetType() );
  223. if ( IsGameConsole() )
  224. {
  225. buf.SeekPut( CUtlBuffer::SEEK_CURRENT, 3 );
  226. }
  227. buf.PutInt( ( pNode->m_eNodeInfo & bits_NODE_SAVE_MASK ) );
  228. buf.PutShort( pNode->GetZone() );
  229. for (int link = 0; link < pNode->NumLinks(); link++)
  230. {
  231. // Only dump if link source
  232. if (node == pNode->GetLinkByIndex(link)->m_iSrcID)
  233. {
  234. totalNumLinks++;
  235. }
  236. }
  237. }
  238. // -------------------------------
  239. // Dump all the links to the file
  240. // -------------------------------
  241. buf.PutInt( totalNumLinks );
  242. for (node = 0; node < m_pNetwork->m_iNumNodes; node++)
  243. {
  244. CAI_Node *pNode = m_pNetwork->GetNode(node);
  245. for (int link = 0; link < pNode->NumLinks(); link++)
  246. {
  247. // Only dump if link source
  248. CAI_Link *pLink = pNode->GetLinkByIndex(link);
  249. if (node == pLink->m_iSrcID)
  250. {
  251. buf.PutShort( pLink->m_iSrcID );
  252. buf.PutShort( pLink->m_iDestID );
  253. buf.Put( pLink->m_iAcceptedMoveTypes, sizeof( pLink->m_iAcceptedMoveTypes) );
  254. }
  255. }
  256. }
  257. // -------------------------------
  258. // Dump WC lookup table
  259. // -------------------------------
  260. CUtlMap<int, int> wcIDs;
  261. SetDefLessFunc(wcIDs);
  262. bool bCheckForProblems = false;
  263. for (node = 0; node < m_pNetwork->m_iNumNodes; node++)
  264. {
  265. int iPreviousNodeBinding = wcIDs.Find( GetEditOps()->m_pNodeIndexTable[node] );
  266. if ( iPreviousNodeBinding != wcIDs.InvalidIndex() )
  267. {
  268. if ( !bCheckForProblems )
  269. {
  270. DevWarning( "******* MAP CONTAINS DUPLICATE HAMMER NODE IDS! CHECK FOR PROBLEMS IN HAMMER TO CORRECT *******\n" );
  271. bCheckForProblems = true;
  272. }
  273. DevWarning( " AI node %d is associated with Hammer node %d, but %d is already bound to node %d\n", node, GetEditOps()->m_pNodeIndexTable[node], GetEditOps()->m_pNodeIndexTable[node], wcIDs[iPreviousNodeBinding] );
  274. }
  275. else
  276. {
  277. wcIDs.Insert( GetEditOps()->m_pNodeIndexTable[node], node );
  278. }
  279. buf.PutInt( GetEditOps()->m_pNodeIndexTable[node] );
  280. }
  281. // -------------------------------
  282. // Write the file out
  283. // -------------------------------
  284. FileHandle_t fh = filesystem->Open( szNrpFilename, "wb" );
  285. if ( !fh )
  286. {
  287. DevWarning( 2, "Couldn't create %s!\n", szNrpFilename );
  288. return;
  289. }
  290. filesystem->Write( buf.Base(), buf.TellPut(), fh );
  291. filesystem->Close(fh);
  292. }
  293. /* Keep this around for debugging
  294. //-----------------------------------------------------------------------------
  295. // Purpose: Only called if network has changed since last time level
  296. // was loaded
  297. // Input :
  298. // Output :
  299. //-----------------------------------------------------------------------------
  300. void CAI_NetworkManager::SaveNetworkGraph( void )
  301. {
  302. // -----------------------------
  303. // Make sure directories have been made
  304. // -----------------------------
  305. char szNrpFilename [MAX_PATH];// text node report filename
  306. Q_strncpy( szNrpFilename, "maps" ,sizeof(szNrpFilename));
  307. filesystem->CreateDirHierarchy( szNrpFilename );
  308. Q_strncat( szNrpFilename, "/graphs", COPY_ALL_CHARACTERS );
  309. filesystem->CreateDirHierarchy( szNrpFilename );
  310. Q_strncat( szNrpFilename, "/", COPY_ALL_CHARACTERS );
  311. Q_strncat( szNrpFilename, STRING( gpGlobals->mapname ), COPY_ALL_CHARACTERS );
  312. Q_strncat( szNrpFilename, ".ain", COPY_ALL_CHARACTERS );
  313. FileHandle_t file = filesystem->Open ( szNrpFilename, "w+" );
  314. // -----------------------------
  315. // Make sure the file opened ok
  316. // -----------------------------
  317. if ( !file )
  318. {
  319. DevWarning( 2, "Couldn't create %s!\n", szNrpFilename );
  320. return;
  321. }
  322. // ---------------------------
  323. // Save the version number
  324. // ---------------------------
  325. filesystem->FPrintf(file,"Version %4d\n",AINET_VERSION_NUMBER);
  326. // -------------------------------
  327. // Dump all the nodes to the file
  328. // -------------------------------
  329. filesystem->FPrintf ( file, "NumNodes: %d\n", m_iNumNodes);
  330. int totalNumLinks = 0;
  331. for (int node = 0; node < m_iNumNodes; node++)
  332. {
  333. filesystem->FPrintf ( file, "Location %4f,%4f,%4f\n",m_pAInode[node]->GetOrigin().x, m_pAInode[node]->GetOrigin().y, m_pAInode[node]->GetOrigin().z );
  334. for (int hull =0;hull<NUM_HULLS;hull++)
  335. {
  336. filesystem->FPrintf ( file, "Voffset %4f\n", m_pAInode[node]->m_flVOffset[hull]);
  337. }
  338. filesystem->FPrintf ( file, "HintType: %4d\n", m_pAInode[node]->m_eHintType );
  339. filesystem->FPrintf ( file, "HintYaw: %4f\n", m_pAInode[node]->GetYaw() );
  340. filesystem->FPrintf ( file, "NodeType %4d\n",m_pAInode[node]->GetType());
  341. filesystem->FPrintf ( file, "NodeInfo %4d\n",m_pAInode[node]->m_eNodeInfo);
  342. filesystem->FPrintf ( file, "Neighbors ");
  343. m_pAInode[node]->m_pNeighborBS->SaveBitString(file);
  344. filesystem->FPrintf ( file, "Visible ");
  345. m_pAInode[node]->m_pVisibleBS->SaveBitString(file);
  346. filesystem->FPrintf ( file, "Connected ");
  347. m_pAInode[node]->m_pConnectedBS->SaveBitString(file);
  348. filesystem->FPrintf ( file, "NumLinks %4d\n",m_pAInode[node]->NumLinks());
  349. for (int link = 0; link < m_pAInode[node]->NumLinks(); link++)
  350. {
  351. // Only dump if link source
  352. if (node == m_pAInode[node]->GetLinkByIndex(link)->m_iSrcID)
  353. {
  354. totalNumLinks++;
  355. }
  356. }
  357. }
  358. // -------------------------------
  359. // Dump all the links to the file
  360. // -------------------------------
  361. filesystem->FPrintf ( file, "TotalNumLinks %4d\n",totalNumLinks);
  362. for (node = 0; node < m_iNumNodes; node++)
  363. {
  364. for (int link = 0; link < m_pAInode[node]->NumLinks(); link++)
  365. {
  366. // Only dump if link source
  367. if (node == m_pAInode[node]->GetLinkByIndex(link)->m_iSrcID)
  368. {
  369. filesystem->FPrintf ( file, "LinkSrcID %4d\n", m_pAInode[node]->GetLinkByIndex(link)->m_iSrcID);
  370. filesystem->FPrintf ( file, "LinkDestID %4d\n", m_pAInode[node]->GetLinkByIndex(link)->m_iDestID);
  371. for (int hull =0;hull<NUM_HULLS;hull++)
  372. {
  373. filesystem->FPrintf ( file, "Hulls %4d\n", m_pAInode[node]->GetLinkByIndex(link)->m_iAcceptedMoveTypes[hull]);
  374. }
  375. }
  376. }
  377. }
  378. // -------------------------------
  379. // Dump WC lookup table
  380. // -------------------------------
  381. for (node = 0; node < m_iNumNodes; node++)
  382. {
  383. filesystem->FPrintf( file, "%4d\n",m_pNodeIndexTable[node]);
  384. }
  385. filesystem->Close(file);
  386. }
  387. */
  388. //-----------------------------------------------------------------------------
  389. // Purpose: Only called if network has changed since last time level
  390. // was loaded
  391. //-----------------------------------------------------------------------------
  392. void CAI_NetworkManager::LoadNetworkGraph( void )
  393. {
  394. #if defined( PORTAL2 )
  395. // not used
  396. return;
  397. #endif
  398. // ---------------------------------------------------
  399. // If I'm in edit mode don't load, always recalculate
  400. // ---------------------------------------------------
  401. if (engine->IsInEditMode())
  402. {
  403. return;
  404. }
  405. if ( !g_pGameRules->FAllowNPCs() )
  406. {
  407. return;
  408. }
  409. // -----------------------------
  410. // Make sure directories have been made
  411. // -----------------------------
  412. char szNrpFilename[MAX_PATH];// text node report filename
  413. Q_strncpy( szNrpFilename, "maps" ,sizeof(szNrpFilename));
  414. filesystem->CreateDirHierarchy( szNrpFilename, "DEFAULT_WRITE_PATH" );
  415. Q_strncat( szNrpFilename, "/graphs", sizeof( szNrpFilename ), COPY_ALL_CHARACTERS );
  416. filesystem->CreateDirHierarchy( szNrpFilename, "DEFAULT_WRITE_PATH" );
  417. Q_strncat( szNrpFilename, "/", sizeof( szNrpFilename ), COPY_ALL_CHARACTERS );
  418. Q_strncat( szNrpFilename, STRING( gpGlobals->mapname ), sizeof( szNrpFilename ), COPY_ALL_CHARACTERS );
  419. Q_strncat( szNrpFilename, PLATFORM_EXT ".ain", sizeof( szNrpFilename ), COPY_ALL_CHARACTERS );
  420. MEM_ALLOC_CREDIT();
  421. // Read the file in one gulp
  422. CUtlBuffer buf;
  423. bool bHaveAIN = false;
  424. if ( IsGameConsole() && g_pQueuedLoader->IsMapLoading() )
  425. {
  426. // .ain was loaded anonymously by bsp, should be ready
  427. void *pData;
  428. int nDataSize;
  429. if ( g_pQueuedLoader->ClaimAnonymousJob( szNrpFilename, &pData, &nDataSize ) )
  430. {
  431. if ( nDataSize != 0 )
  432. {
  433. buf.Put( pData, nDataSize );
  434. bHaveAIN = true;
  435. }
  436. filesystem->FreeOptimalReadBuffer( pData );
  437. }
  438. }
  439. if ( !bHaveAIN && !filesystem->ReadFile( szNrpFilename, "game", buf ) )
  440. {
  441. DevWarning( 2, "Couldn't read %s!\n", szNrpFilename );
  442. return;
  443. }
  444. // ---------------------------
  445. // Check the version number
  446. // ---------------------------
  447. if ( buf.GetChar() == 'V' && buf.GetChar() == 'e' && buf.GetChar() == 'r' )
  448. {
  449. DevMsg( "AI node graph %s is out of date\n", szNrpFilename );
  450. return;
  451. }
  452. buf.SeekGet( CUtlBuffer::SEEK_HEAD, 0 );
  453. int version = buf.GetInt();
  454. if ( version != AINET_VERSION_NUMBER)
  455. {
  456. DevMsg( "AI node graph %s is out of date\n", szNrpFilename );
  457. return;
  458. }
  459. int mapversion = buf.GetInt();
  460. if ( mapversion != gpGlobals->mapversion && !g_ai_norebuildgraph.GetBool() )
  461. {
  462. DevMsg( "AI node graph %s is out of date (map version changed)\n", szNrpFilename );
  463. return;
  464. }
  465. // ----------------------------------------
  466. // Get the network size and allocate space
  467. // ----------------------------------------
  468. int numNodes = buf.GetInt();
  469. if ( numNodes > MAX_NODES || numNodes < 0 )
  470. {
  471. Error( "AI node graph %s is corrupt\n", szNrpFilename );
  472. DevMsg( "%s", (const char *)buf.Base() );
  473. DevMsg( "\n" );
  474. Assert( 0 );
  475. return;
  476. }
  477. // ------------------------------------------------------------------------
  478. // If in wc_edit mode allocate extra space for nodes that might be created
  479. // ------------------------------------------------------------------------
  480. if ( engine->IsInEditMode() )
  481. {
  482. numNodes = MAX( numNodes, 1024 );
  483. }
  484. m_pNetwork->m_pAInode = new CAI_Node*[MAX( numNodes, 1 )];
  485. memset( m_pNetwork->m_pAInode, 0, sizeof( CAI_Node* ) * MAX( numNodes, 1 ) );
  486. // -------------------------------
  487. // Load all the nodes to the file
  488. // -------------------------------
  489. int node;
  490. for ( node = 0; node < numNodes; node++)
  491. {
  492. Vector origin;
  493. float yaw;
  494. origin.x = buf.GetFloat();
  495. origin.y = buf.GetFloat();
  496. origin.z = buf.GetFloat();
  497. yaw = buf.GetFloat();
  498. CAI_Node *new_node = m_pNetwork->AddNode( origin, yaw );
  499. buf.Get( new_node->m_flVOffset, sizeof(new_node->m_flVOffset) );
  500. new_node->m_eNodeType = (NodeType_e)buf.GetChar();
  501. if ( IsGameConsole() )
  502. {
  503. buf.SeekGet( CUtlBuffer::SEEK_CURRENT, 3 );
  504. }
  505. new_node->m_eNodeInfo = buf.GetInt();
  506. new_node->m_zone = buf.GetShort();
  507. }
  508. // -------------------------------
  509. // Load all the links to the fild
  510. // -------------------------------
  511. int totalNumLinks = buf.GetInt();
  512. for (int link = 0; link < totalNumLinks; link++)
  513. {
  514. int srcID, destID;
  515. srcID = buf.GetShort();
  516. destID = buf.GetShort();
  517. CAI_Link *pLink = m_pNetwork->CreateLink( srcID, destID );;
  518. byte ignored[NUM_HULLS];
  519. byte *pDest = ( pLink ) ? &pLink->m_iAcceptedMoveTypes[0] : &ignored[0];
  520. buf.Get( pDest, sizeof(ignored) );
  521. }
  522. // -------------------------------
  523. // Load WC lookup table
  524. // -------------------------------
  525. delete [] GetEditOps()->m_pNodeIndexTable;
  526. GetEditOps()->m_pNodeIndexTable = new int[MAX( m_pNetwork->m_iNumNodes, 1 )];
  527. memset( GetEditOps()->m_pNodeIndexTable, 0, sizeof( int ) *MAX( m_pNetwork->m_iNumNodes, 1 ) );
  528. for (node = 0; node < m_pNetwork->m_iNumNodes; node++)
  529. {
  530. GetEditOps()->m_pNodeIndexTable[node] = buf.GetInt();
  531. }
  532. #if 1
  533. CUtlRBTree<int> usedIds;
  534. CUtlRBTree<int> reportedIds;
  535. SetDefLessFunc( usedIds );
  536. SetDefLessFunc( reportedIds );
  537. bool printedHeader = false;
  538. for (node = 0; node < m_pNetwork->m_iNumNodes; node++)
  539. {
  540. int editorId = GetEditOps()->m_pNodeIndexTable[node];
  541. if ( editorId != NO_NODE )
  542. {
  543. if ( usedIds.Find( editorId ) != usedIds.InvalidIndex() )
  544. {
  545. if ( !printedHeader )
  546. {
  547. Warning( "** Duplicate Hammer Node IDs: " );
  548. printedHeader = true;
  549. }
  550. if ( reportedIds.Find( editorId ) == reportedIds.InvalidIndex() )
  551. {
  552. DevMsg( "%d, ", editorId );
  553. reportedIds.Insert( editorId );
  554. }
  555. }
  556. else
  557. usedIds.Insert( editorId );
  558. }
  559. }
  560. if ( printedHeader )
  561. DevMsg( "\n** Should run \"Check For Problems\" on the VMF then verify dynamic links\n" );
  562. #endif
  563. gm_fNetworksLoaded = true;
  564. CAI_DynamicLink::gm_bInitialized = false;
  565. }
  566. /* Keep this around for debugging
  567. //-----------------------------------------------------------------------------
  568. // Purpose: Only called if network has changed since last time level
  569. // was loaded
  570. // Input :
  571. // Output :
  572. //-----------------------------------------------------------------------------
  573. void CAI_NetworkManager::LoadNetworkGraph( void )
  574. {
  575. // ---------------------------------------------------
  576. // If I'm in edit mode don't load, always recalculate
  577. // ---------------------------------------------------
  578. if (engine->IsInEditMode())
  579. {
  580. return;
  581. }
  582. // -----------------------------
  583. // Make sure directories have been made
  584. // -----------------------------
  585. char szNrpFilename [MAX_PATH];// text node report filename
  586. Q_strncpy( szNrpFilename, "maps" ,sizeof(szNrpFilename));
  587. filesystem->CreateDirHierarchy( szNrpFilename );
  588. Q_strncat( szNrpFilename, "/graphs", COPY_ALL_CHARACTERS );
  589. filesystem->CreateDirHierarchy( szNrpFilename );
  590. Q_strncat( szNrpFilename, "/", COPY_ALL_CHARACTERS );
  591. Q_strncat( szNrpFilename, STRING( gpGlobals->mapname ), COPY_ALL_CHARACTERS );
  592. Q_strncat( szNrpFilename, ".ain", COPY_ALL_CHARACTERS );
  593. FileHandle_t file = filesystem->Open ( szNrpFilename, "r" );
  594. // -----------------------------
  595. // Make sure the file opened ok
  596. // -----------------------------
  597. if ( !file )
  598. {
  599. DevWarning( 2, "Couldn't create %s!\n", szNrpFilename );
  600. return;
  601. }
  602. // ---------------------------
  603. // Check the version number
  604. // ---------------------------
  605. char temps[256];
  606. int version;
  607. fscanf(file,"%255s",&temps);
  608. fscanf(file, "%i\n",&version);
  609. if (version!=AINET_VERSION_NUMBER)
  610. {
  611. return;
  612. }
  613. // ----------------------------------------
  614. // Get the network size and allocate space
  615. // ----------------------------------------
  616. int numNodes;
  617. fscanf(file,"%255s",&temps);
  618. fscanf ( file, "%d\n", &numNodes);
  619. // ------------------------------------------------------------------------
  620. // If in wc_edit mode allocate extra space for nodes that might be created
  621. // ------------------------------------------------------------------------
  622. if ( engine->IsInEditMode() )
  623. {
  624. numNodes = MAX( numNodes, 1024 );
  625. }
  626. m_pAInode = new CAI_Node*[numNodes];
  627. if ( !m_pAInode )
  628. {
  629. Warning( "LoadNetworkGraph: Not enough memory to create %i nodes\n", numNodes );
  630. Assert(0);
  631. return;
  632. }
  633. // -------------------------------
  634. // Load all the nodes to the file
  635. // -------------------------------
  636. for (int node = 0; node < numNodes; node++)
  637. {
  638. CAI_Node *new_node = AddNode();
  639. Vector origin;
  640. fscanf(file,"%255s",&temps);
  641. fscanf(file, "%f,%f,%f\n", &new_node->GetOrigin().x, &new_node->GetOrigin().y, &new_node->GetOrigin().z );
  642. for (int hull =0;hull<NUM_HULLS;hull++)
  643. {
  644. fscanf(file,"%255s",&temps);
  645. fscanf(file, "%f\n", &new_node->m_flVOffset[hull]);
  646. }
  647. fscanf(file,"%255s",&temps);
  648. fscanf(file, "%d\n", &new_node->m_eHintType );
  649. fscanf(file,"%255s",&temps);
  650. fscanf(file, "%f\n", &new_node->GetYaw() );
  651. fscanf(file,"%255s",&temps);
  652. fscanf(file, "%d\n",&new_node->GetType());
  653. fscanf(file,"%255s",&temps);
  654. fscanf(file, "%d\n",&new_node->m_eNodeInfo);
  655. fscanf(file,"%255s",&temps);
  656. new_node->m_pNeighborBS = new CVarBitVec(numNodes);
  657. new_node->m_pNeighborBS->LoadBitString(file);
  658. fscanf(file,"%255s",&temps);
  659. new_node->m_pVisibleBS = new CVarBitVec(numNodes);
  660. new_node->m_pVisibleBS->LoadBitString(file);
  661. fscanf(file,"%255s",&temps);
  662. new_node->m_pConnectedBS = new CVarBitVec(numNodes);
  663. new_node->m_pConnectedBS->LoadBitString(file);
  664. fscanf(file,"%255s",&temps);
  665. int numLinks;
  666. fscanf (file, "%4d",&numLinks);
  667. // ------------------------------------------------------------------------
  668. // If in wc_edit mode allocate extra space for nodes that might be created
  669. // ------------------------------------------------------------------------
  670. if ( engine->IsInEditMode() )
  671. {
  672. numLinks = AI_MAX_NODE_LINKS;
  673. }
  674. //Assert ( numLinks >= 1 );
  675. new_node->AllocateLinkSpace( numLinks );
  676. }
  677. // -------------------------------
  678. // Load all the links to the fild
  679. // -------------------------------
  680. int totalNumLinks;
  681. fscanf(file,"%255s",&temps);
  682. fscanf ( file, "%d\n",&totalNumLinks);
  683. for (int link = 0; link < totalNumLinks; link++)
  684. {
  685. CAI_Link *new_link = new CAI_Link;
  686. fscanf(file,"%255s",&temps);
  687. fscanf ( file, "%4d\n", &new_link->m_iSrcID);
  688. fscanf(file,"%255s",&temps);
  689. fscanf ( file, "%4d\n", &new_link->m_iDestID);
  690. for (int hull =0;hull<NUM_HULLS;hull++)
  691. {
  692. fscanf(file,"%255s",&temps);
  693. fscanf ( file, "%d\n", &new_link->m_iAcceptedMoveTypes[hull]);
  694. }
  695. // Now add link to source and destination nodes
  696. m_pAInode[new_link->m_iSrcID]->AddLink(new_link);
  697. m_pAInode[new_link->m_iDestID]->AddLink(new_link);
  698. }
  699. // -------------------------------
  700. // Load WC lookup table
  701. // -------------------------------
  702. m_pNodeIndexTable = new int[m_iNumNodes];
  703. for (node = 0; node < m_iNumNodes; node++)
  704. {
  705. fscanf( file, "%d\n",&m_pNodeIndexTable[node]);
  706. }
  707. CAI_NetworkManager::NetworksLoaded() = true;
  708. fclose(file);
  709. }
  710. */
  711. //-----------------------------------------------------------------------------
  712. // Purpose: Deletes all AINetworks from memory
  713. //-----------------------------------------------------------------------------
  714. void CAI_NetworkManager::DeleteAllAINetworks(void)
  715. {
  716. CAI_DynamicLink::gm_bInitialized = false;
  717. gm_fNetworksLoaded = false;
  718. g_pBigAINet = NULL;
  719. }
  720. //-----------------------------------------------------------------------------
  721. // Purpose: Only called if network has changed since last time level
  722. // was loaded
  723. //-----------------------------------------------------------------------------
  724. void CAI_NetworkManager::BuildNetworkGraph( void )
  725. {
  726. if ( m_bDontSaveGraph )
  727. return;
  728. CAI_DynamicLink::gm_bInitialized = false;
  729. g_AINetworkBuilder.Build( m_pNetwork );
  730. // If I'm loading for the first time save. Otherwise I'm
  731. // doing a wc edit and I don't want to save
  732. if (!CAI_NetworkManager::NetworksLoaded())
  733. {
  734. SaveNetworkGraph();
  735. gm_fNetworksLoaded = true;
  736. }
  737. }
  738. //------------------------------------------------------------------------------
  739. bool g_bAIDisabledByUser = false;
  740. void CAI_NetworkManager::InitializeAINetworks()
  741. {
  742. // For not just create a single AI Network called "BigNet"
  743. // At some later point we may have mulitple AI networks
  744. CAI_NetworkManager *pNetwork;
  745. g_pAINetworkManager = pNetwork = CREATE_ENTITY( CAI_NetworkManager, "ai_network" );
  746. pNetwork->AddEFlags( EFL_KEEP_ON_RECREATE_ENTITIES );
  747. g_pBigAINet = pNetwork->GetNetwork();
  748. pNetwork->SetName( AllocPooledString("BigNet") );
  749. pNetwork->Spawn();
  750. if ( engine->IsInEditMode() )
  751. {
  752. g_ai_norebuildgraph.SetValue( 0 );
  753. }
  754. if ( CAI_NetworkManager::IsAIFileCurrent( STRING( gpGlobals->mapname ) ) )
  755. {
  756. pNetwork->LoadNetworkGraph();
  757. if ( !g_bAIDisabledByUser )
  758. {
  759. CAI_BaseNPC::m_nDebugBits &= ~bits_debugDisableAI;
  760. }
  761. }
  762. // Reset node counter used during load
  763. CNodeEnt::m_nNodeCount = 0;
  764. if ( g_ai_threadedgraphbuild.GetBool() && !engine->IsInEditMode() )
  765. {
  766. pNetwork->SetThink( &CAI_NetworkManager::ThreadedInit );
  767. }
  768. else
  769. {
  770. pNetwork->SetThink( &CAI_NetworkManager::DelayedInit );
  771. }
  772. pNetwork->SetNextThink( gpGlobals->curtime );
  773. }
  774. // UNDONE: Where should this be defined?
  775. #ifndef MAX_PATH
  776. #define MAX_PATH 256
  777. #endif
  778. //-----------------------------------------------------------------------------
  779. // Purpose: Returns true if the AINetwork data files are up to date
  780. //-----------------------------------------------------------------------------
  781. bool CAI_NetworkManager::IsAIFileCurrent ( const char *szMapName )
  782. {
  783. char szBspFilename[MAX_PATH];
  784. char szGraphFilename[MAX_PATH];
  785. if ( !g_pGameRules->FAllowNPCs() )
  786. {
  787. return false;
  788. }
  789. if ( IsGameConsole() && ( filesystem->GetDVDMode() == DVDMODE_STRICT ) )
  790. {
  791. // dvd build process validates and guarantees correctness, timestamps are allowed to be wrong
  792. return true;
  793. }
  794. Q_snprintf( szBspFilename, sizeof( szBspFilename ), "maps/%s%s.bsp" ,szMapName, GetPlatformExt() );
  795. Q_snprintf( szGraphFilename, sizeof( szGraphFilename ), "maps/graphs/%s%s.ain", szMapName, GetPlatformExt() );
  796. int iCompare;
  797. if ( engine->CompareFileTime( szBspFilename, szGraphFilename, &iCompare ) )
  798. {
  799. if ( iCompare > 0 )
  800. {
  801. // BSP file is newer.
  802. if ( g_ai_norebuildgraph.GetInt() )
  803. {
  804. // The user has specified that they wish to override the
  805. // rebuilding of outdated nodegraphs (see top of this file)
  806. if ( filesystem->FileExists( szGraphFilename ) )
  807. {
  808. // Display these messages only if the graph exists, and the
  809. // user is asking to override the rebuilding. If the graph does
  810. // not exist, we're going to build it whether the user wants to or
  811. // not.
  812. DevMsg( 2, ".AIN File will *NOT* be updated. User Override.\n\n" );
  813. DevMsg( "\n*****Node Graph Rebuild OVERRIDDEN by user*****\n\n" );
  814. }
  815. return true;
  816. }
  817. else
  818. {
  819. // Graph is out of date. Rebuild at usual.
  820. DevMsg( 2, ".AIN File will be updated\n\n" );
  821. return false;
  822. }
  823. }
  824. return true;
  825. }
  826. return false;
  827. }
  828. //------------------------------------------------------------------------------
  829. void CAI_NetworkManager::Spawn ( void )
  830. {
  831. SetSolid( SOLID_NONE );
  832. SetMoveType( MOVETYPE_NONE );
  833. }
  834. //------------------------------------------------------------------------------
  835. static bool SetupEditMode()
  836. {
  837. // CSGO disables regular NPC AI but we still want to support hammer_update_safe_entities so init this in edit mode
  838. if (engine->IsInEditMode())
  839. {
  840. int status = Editor_BeginSession(STRING(gpGlobals->mapname), gpGlobals->mapversion, false);
  841. if (status == Editor_NotRunning)
  842. {
  843. DevMsg("\nAborting map_edit\nWorldcraft not running...\n\n");
  844. UTIL_CenterPrintAll( "Worldcraft not running...\n" );
  845. engine->ServerCommand("disconnect\n");
  846. }
  847. else if (status == Editor_BadCommand)
  848. {
  849. DevMsg("\nAborting map_edit\nWC/Engine map versions different...\n\n");
  850. UTIL_CenterPrintAll( "WC/Engine map versions different...\n" );
  851. engine->ServerCommand("disconnect\n");
  852. }
  853. else
  854. {
  855. // Increment version number when session begins
  856. gpGlobals->mapversion++;
  857. return true;
  858. }
  859. }
  860. return false;
  861. }
  862. void CAI_NetworkManager::DelayedInit( void )
  863. {
  864. #if defined( PORTAL2 )
  865. SetThink ( NULL );
  866. m_fInitalized = true;
  867. #else
  868. if ( !g_pGameRules->FAllowNPCs() )
  869. {
  870. SetThink ( NULL );
  871. SetupEditMode();
  872. return;
  873. }
  874. if ( !g_ai_norebuildgraph.GetInt() )
  875. {
  876. // ----------------------------------------------------------
  877. // Actually enter DelayedInit twice when rebuilding the
  878. // node graph. The first time through we just print the
  879. // warning message. We only actually do the rebuild on
  880. // the second pass to make sure the message hits the screen
  881. // ----------------------------------------------------------
  882. if (m_bNeedGraphRebuild)
  883. {
  884. Assert( !m_bDontSaveGraph );
  885. BuildNetworkGraph(); // For now only one AI Network
  886. if (engine->IsInEditMode())
  887. {
  888. engine->ServerCommand("exec map_edit.cfg\n");
  889. }
  890. SetThink ( NULL );
  891. if ( !g_bAIDisabledByUser )
  892. {
  893. CAI_BaseNPC::m_nDebugBits &= ~bits_debugDisableAI;
  894. }
  895. }
  896. // --------------------------------------------
  897. // If I haven't loaded a network, or I'm in
  898. // WorldCraft edit mode rebuild the network
  899. // --------------------------------------------
  900. else if ( !m_bDontSaveGraph && ( !CAI_NetworkManager::NetworksLoaded() || engine->IsInEditMode() ) )
  901. {
  902. #ifdef _WIN32
  903. // --------------------------------------------------------
  904. // If in edit mode start WC session and make sure we are
  905. // running the same map in WC and the engine
  906. // --------------------------------------------------------
  907. if (engine->IsInEditMode())
  908. {
  909. if ( !SetupEditMode() )
  910. {
  911. SetThink( NULL );
  912. return;
  913. }
  914. }
  915. #endif
  916. DevMsg( "Node Graph out of Date. Rebuilding...\n" );
  917. if ( developer.GetInt() )
  918. {
  919. UTIL_CenterPrintAll( "Node Graph out of Date. Rebuilding...\n" );
  920. }
  921. m_bNeedGraphRebuild = true;
  922. g_pAINetworkManager->SetNextThink( gpGlobals->curtime + 1 );
  923. return;
  924. }
  925. }
  926. // --------------------------------------------
  927. // Initialize any dynamic links
  928. // --------------------------------------------
  929. CAI_DynamicLink::InitDynamicLinks();
  930. FixupHints();
  931. GetEditOps()->OnInit();
  932. m_fInitalized = true;
  933. if ( g_AI_Manager.NumAIs() != 0 && g_pBigAINet->NumNodes() == 0 )
  934. DevMsg( "WARNING: Level contains NPCs but has no path nodes\n" );
  935. #endif
  936. }
  937. //------------------------------------------------------------------------------
  938. void CAI_NetworkManager::ThreadedInit( void )
  939. {
  940. Assert( !engine->IsInEditMode() );
  941. if ( !g_pGameRules->FAllowNPCs() )
  942. {
  943. SetThink ( NULL );
  944. return;
  945. }
  946. if ( m_bDontSaveGraph )
  947. {
  948. Assert( !m_bDontSaveGraph );
  949. Warning( "m_bDontSaveGraph set, using synchronous map rebuild\n" );
  950. SetThink( &CAI_NetworkManager::DelayedInit );
  951. SetNextThink(gpGlobals->curtime);
  952. return DelayedInit();
  953. }
  954. // We have three stages:
  955. // * graph not built yet
  956. // * graph building underway
  957. // * graph built
  958. if ( !CAI_NetworkManager::NetworksLoaded() )
  959. {
  960. // maps not loaded from disk.
  961. switch ( m_ThreadedBuild.nBuildStage )
  962. {
  963. case ThreadedGraphBuildData::BUILD_NOT_STARTED:
  964. {
  965. // seize the mutex while I set up the struct
  966. if ( !m_ThreadedBuild.mutex.TryLock() )
  967. {
  968. AssertMsg( false, "FAILED to initiate threaded node graph build due to already locked mutex!\n" );
  969. Warning( "FAILED to initiate threaded node graph build due to already locked mutex!" );
  970. return;
  971. }
  972. // kick off a threaded map build. first, temporarily reset the global ai network
  973. // pointer as a hack so all ais think it doesn't exist yet
  974. m_ThreadedBuild.pBuildingNetwork = GetNetwork();
  975. //#pragma message("Warning: find some way to prevent AI from using network before it's ready, but allowing the TestHull to still instantiate.")
  976. // g_pBigAINet = m_pNetwork = NULL;
  977. // // fire off a thread to build the map
  978. m_ThreadedBuild.nBuildStage = ThreadedGraphBuildData::BUILD_UNDERWAY;
  979. m_ThreadedBuild.job = CreateSimpleThread( ThreadedBuildJob, &m_ThreadedBuild );
  980. // could SetThreadAffinity...
  981. CAI_DynamicLink::gm_bInitialized = false;
  982. // and off you go!
  983. m_ThreadedBuild.mutex.Unlock();
  984. // print a "building" message
  985. UTIL_CenterPrintAll( "Node Graph out of Date. Rebuilding in background.\n" );
  986. SetNextThink( gpGlobals->curtime + 1 );
  987. return;
  988. break;
  989. }
  990. case ThreadedGraphBuildData::BUILD_UNDERWAY:
  991. {
  992. // print a "building" message
  993. UTIL_CenterPrintAll( "Node Graph out of Date. Rebuilding in background.\n" );
  994. SetNextThink( gpGlobals->curtime + 1 );
  995. return;
  996. break;
  997. }
  998. case ThreadedGraphBuildData::BUILD_DONE:
  999. // grab the lock again to make sure we're done
  1000. if ( !m_ThreadedBuild.mutex.TryLock() )
  1001. {
  1002. AssertMsg( false, "Had to wait for threaded node graph build lock even though it was supposedly done." );
  1003. m_ThreadedBuild.mutex.Lock();
  1004. }
  1005. // set the global pointers again
  1006. g_pBigAINet = m_pNetwork = m_ThreadedBuild.pBuildingNetwork;
  1007. m_ThreadedBuild.pBuildingNetwork = NULL;
  1008. ReleaseThreadHandle( m_ThreadedBuild.job );
  1009. m_ThreadedBuild.job = NULL;
  1010. // If I'm loading for the first time save. Otherwise I'm
  1011. // doing a wc edit and I don't want to save
  1012. if (!CAI_NetworkManager::NetworksLoaded())
  1013. {
  1014. SaveNetworkGraph();
  1015. gm_fNetworksLoaded = true;
  1016. }
  1017. // --------------------------------------------
  1018. // Initialize any dynamic links
  1019. // --------------------------------------------
  1020. CAI_DynamicLink::InitDynamicLinks();
  1021. FixupHints();
  1022. break;
  1023. default:
  1024. AssertMsg1( false, "Invalid threaded node graph build stage %d!\n", (int) m_ThreadedBuild.nBuildStage );
  1025. }
  1026. }
  1027. // job's done
  1028. GetEditOps()->OnInit();
  1029. m_fInitalized = true;
  1030. if ( g_AI_Manager.NumAIs() != 0 && g_pBigAINet->NumNodes() == 0 )
  1031. DevMsg( "WARNING: Level contains NPCs but has no path nodes\n" );
  1032. SetThink ( NULL );
  1033. }
  1034. //------------------------------------------------------------------------------
  1035. void CAI_NetworkManager::FixupHints()
  1036. {
  1037. AIHintIter_t iter;
  1038. CAI_Hint *pHint = CAI_HintManager::GetFirstHint( &iter );
  1039. while ( pHint )
  1040. {
  1041. pHint->FixupTargetNode();
  1042. pHint = CAI_HintManager::GetNextHint( &iter );
  1043. }
  1044. }
  1045. uintp CAI_NetworkManager::ThreadedBuildJob( /* (ThreadedGraphBuildData *) */ void *pBuildData )
  1046. {
  1047. ThreadedGraphBuildData * RESTRICT pBuild = reinterpret_cast<ThreadedGraphBuildData *>(pBuildData);
  1048. pBuild->mutex.Lock();
  1049. g_AINetworkBuilder.Build( pBuild->pBuildingNetwork );
  1050. pBuild->nBuildStage = ThreadedGraphBuildData::BUILD_DONE;
  1051. pBuild->mutex.Unlock();
  1052. return 0;
  1053. }
  1054. //-----------------------------------------------------------------------------
  1055. // CAI_NetworkEditTools
  1056. //-----------------------------------------------------------------------------
  1057. CAI_Node* CAI_NetworkEditTools::m_pLastDeletedNode = NULL; // For undo in wc edit mode
  1058. int CAI_NetworkEditTools::m_iHullDrawNum = HULL_HUMAN; // Which hulls to draw
  1059. int CAI_NetworkEditTools::m_iVisibilityNode = NO_NODE;
  1060. int CAI_NetworkEditTools::m_iGConnectivityNode = NO_NODE;
  1061. bool CAI_NetworkEditTools::m_bAirEditMode = false;
  1062. bool CAI_NetworkEditTools::m_bLinkEditMode = false;
  1063. float CAI_NetworkEditTools::m_flAirEditDistance = 300;
  1064. #ifdef AI_PERF_MON
  1065. // Performance stats (only for development)
  1066. int CAI_NetworkEditTools::m_nPerfStatNN = 0;
  1067. int CAI_NetworkEditTools::m_nPerfStatPB = 0;
  1068. float CAI_NetworkEditTools::m_fNextPerfStatTime = -1;
  1069. #endif
  1070. //------------------------------------------------------------------------------
  1071. void CAI_NetworkEditTools::OnInit()
  1072. {
  1073. // --------------------------------------------
  1074. // If I'm not in edit mode delete WC ID table
  1075. // --------------------------------------------
  1076. if ( !engine->IsInEditMode() )
  1077. {
  1078. // delete[] m_pNodeIndexTable; // For now only one AI Network called "BigNet"
  1079. // m_pNodeIndexTable = NULL;
  1080. }
  1081. }
  1082. //------------------------------------------------------------------------------
  1083. // Purpose : Given a WorldCraft node ID, return the associated engine ID
  1084. // Input :
  1085. // Output :
  1086. //------------------------------------------------------------------------------
  1087. int CAI_NetworkEditTools::GetNodeIdFromWCId( int nWCId )
  1088. {
  1089. if ( nWCId == -1 )
  1090. return -1;
  1091. if (!m_pNodeIndexTable)
  1092. {
  1093. DevMsg("ERROR: Trying to get WC ID with no table!\n");
  1094. return -1;
  1095. }
  1096. if (!m_pNetwork->NumNodes())
  1097. {
  1098. DevMsg("ERROR: Trying to get WC ID with no network!\n");
  1099. return -1;
  1100. }
  1101. for (int i=0;i<m_pNetwork->NumNodes();i++)
  1102. {
  1103. if (m_pNodeIndexTable[i] == nWCId)
  1104. {
  1105. return i;
  1106. }
  1107. }
  1108. return -1;
  1109. }
  1110. //-----------------------------------------------------------------------------
  1111. int CAI_NetworkEditTools::GetWCIdFromNodeId( int nNodeId )
  1112. {
  1113. if ( nNodeId == -1 || nNodeId >= m_pNetwork->NumNodes() )
  1114. return -1;
  1115. return m_pNodeIndexTable[nNodeId];
  1116. }
  1117. //-----------------------------------------------------------------------------
  1118. // Purpose: Find the nearest ainode that is faced from the given location
  1119. // and within the angular threshold (ignores worldspawn).
  1120. // DO NOT USE FOR ANY RUN TIME RELEASE CODE
  1121. // Used for selection of nodes in debugging display only!
  1122. //
  1123. //-----------------------------------------------------------------------------
  1124. CAI_Node *CAI_NetworkEditTools::FindAINodeNearestFacing( const Vector &origin, const Vector &facing, float threshold, int nNodeType)
  1125. {
  1126. float bestDot = threshold;
  1127. CAI_Node *best = NULL;
  1128. CAI_Network* aiNet = g_pBigAINet;
  1129. for (int node =0; node < aiNet->NumNodes();node++)
  1130. {
  1131. if (aiNet->GetNode(node)->GetType() != NODE_DELETED)
  1132. {
  1133. // Pick nodes that are in the current editing type
  1134. if ( nNodeType == NODE_ANY ||
  1135. nNodeType == aiNet->GetNode(node)->GetType() )
  1136. {
  1137. // Make vector to node
  1138. Vector to_node = (aiNet->GetNode(node)->GetPosition(m_iHullDrawNum) - origin);
  1139. VectorNormalize( to_node );
  1140. float dot = DotProduct (facing , to_node );
  1141. if (dot > bestDot)
  1142. {
  1143. // Make sure I have a line of sight to it
  1144. trace_t tr;
  1145. AI_TraceLine ( origin, aiNet->GetNode(node)->GetPosition(m_iHullDrawNum),
  1146. MASK_BLOCKLOS, NULL, COLLISION_GROUP_NONE, &tr );
  1147. if ( tr.fraction == 1.0 )
  1148. {
  1149. bestDot = dot;
  1150. best = aiNet->GetNode(node);
  1151. }
  1152. }
  1153. }
  1154. }
  1155. }
  1156. return best;
  1157. }
  1158. Vector PointOnLineNearestPoint(const Vector& vStartPos, const Vector& vEndPos, const Vector& vPoint)
  1159. {
  1160. Vector vEndToStart = (vEndPos - vStartPos);
  1161. Vector vOrgToStart = (vPoint - vStartPos);
  1162. float fNumerator = DotProduct(vEndToStart,vOrgToStart);
  1163. float fDenominator = vEndToStart.Length() * vOrgToStart.Length();
  1164. float fIntersectDist = vOrgToStart.Length()*(fNumerator/fDenominator);
  1165. VectorNormalize( vEndToStart );
  1166. Vector vIntersectPos = vStartPos + vEndToStart * fIntersectDist;
  1167. return vIntersectPos;
  1168. }
  1169. //-----------------------------------------------------------------------------
  1170. // Purpose: Find the nearest ainode that is faced from the given location
  1171. // and within the angular threshold (ignores worldspawn).
  1172. // DO NOT USE FOR ANY RUN TIME RELEASE CODE
  1173. // Used for selection of nodes in debugging display only!
  1174. //-----------------------------------------------------------------------------
  1175. CAI_Link *CAI_NetworkEditTools::FindAILinkNearestFacing( const Vector &vOrigin, const Vector &vFacing, float threshold)
  1176. {
  1177. float bestDot = threshold;
  1178. CAI_Link *best = NULL;
  1179. CAI_Network* aiNet = g_pBigAINet;
  1180. for (int node =0; node < aiNet->NumNodes();node++)
  1181. {
  1182. if (aiNet->GetNode(node)->GetType() != NODE_DELETED)
  1183. {
  1184. // Pick nodes that are in the current editing type
  1185. if (( m_bAirEditMode && aiNet->GetNode(node)->GetType() == NODE_AIR) ||
  1186. (!m_bAirEditMode && aiNet->GetNode(node)->GetType() == NODE_GROUND))
  1187. {
  1188. // Go through each link
  1189. for (int link=0; link < aiNet->GetNode(node)->NumLinks();link++)
  1190. {
  1191. CAI_Link *nodeLink = aiNet->GetNode(node)->GetLinkByIndex(link);
  1192. // Find position on link that I am looking
  1193. int endID = nodeLink->DestNodeID(node);
  1194. Vector startPos = aiNet->GetNode(node)->GetPosition(m_iHullDrawNum);
  1195. Vector endPos = aiNet->GetNode(endID)->GetPosition(m_iHullDrawNum);
  1196. Vector vNearest = PointOnLineNearestPoint(startPos, endPos, vOrigin);
  1197. // Get angle between viewing dir. and nearest point on line
  1198. Vector vOriginToNearest = (vNearest - vOrigin);
  1199. float fNumerator = DotProduct(vOriginToNearest,vFacing);
  1200. float fDenominator = vOriginToNearest.Length();
  1201. float fAngleToNearest = acos(fNumerator/fDenominator);
  1202. // If not facing the line reject
  1203. if (fAngleToNearest > 1.57)
  1204. {
  1205. continue;
  1206. }
  1207. // Calculate intersection of facing direction to nearest point
  1208. float fIntersectDist = vOriginToNearest.Length() * tan(fAngleToNearest);
  1209. Vector dir = endPos-startPos;
  1210. float fLineLen = VectorNormalize( dir );
  1211. Vector vIntersection = vNearest + (fIntersectDist * dir);
  1212. // Reject of beyond end of line
  1213. if (((vIntersection - startPos).Length() > fLineLen) ||
  1214. ((vIntersection - endPos ).Length() > fLineLen) )
  1215. {
  1216. continue;
  1217. }
  1218. // Now test dot to the look position
  1219. Vector toLink = vIntersection - vOrigin;
  1220. VectorNormalize(toLink);
  1221. float lookDot = DotProduct (vFacing , toLink);
  1222. if (lookDot > bestDot)
  1223. {
  1224. // Make sure I have a line of sight to it
  1225. trace_t tr;
  1226. AI_TraceLine ( vOrigin, vIntersection, MASK_BLOCKLOS, NULL, COLLISION_GROUP_NONE, &tr );
  1227. if ( tr.fraction == 1.0 )
  1228. {
  1229. bestDot = lookDot;
  1230. best = nodeLink;
  1231. }
  1232. }
  1233. }
  1234. }
  1235. }
  1236. }
  1237. return best;
  1238. }
  1239. //-----------------------------------------------------------------------------
  1240. // Purpose: Used for WC edit more. Marks that the network should be
  1241. // rebuild and turns of any displays that have been invalidated
  1242. // as the network is now out of date
  1243. // Input :
  1244. // Output :
  1245. //-----------------------------------------------------------------------------
  1246. void CAI_NetworkEditTools::SetRebuildFlags( void )
  1247. {
  1248. m_debugNetOverlays |= bits_debugNeedRebuild;
  1249. m_debugNetOverlays &= ~bits_debugOverlayConnections;
  1250. m_debugNetOverlays &= ~bits_debugOverlayGraphConnect;
  1251. m_debugNetOverlays &= ~bits_debugOverlayVisibility;
  1252. m_debugNetOverlays &= ~bits_debugOverlayHulls;
  1253. // Not allowed to edit links when graph outdated
  1254. m_bLinkEditMode = false;
  1255. }
  1256. //-----------------------------------------------------------------------------
  1257. // Purpose: Used for WC edit more. After node graph has been rebuild
  1258. // marks it as so and turns connection display back on
  1259. // Input :
  1260. // Output :
  1261. //-----------------------------------------------------------------------------
  1262. void CAI_NetworkEditTools::ClearRebuildFlags( void )
  1263. {
  1264. m_debugNetOverlays |= bits_debugOverlayConnections;
  1265. // ------------------------------------------
  1266. // Clear all rebuild flags in nodes
  1267. // ------------------------------------------
  1268. for (int i = 0; i < m_pNetwork->NumNodes(); i++)
  1269. {
  1270. m_pNetwork->GetNode(i)->m_eNodeInfo &= ~bits_NODE_WC_CHANGED;
  1271. m_pNetwork->GetNode(i)->ClearNeedsRebuild();
  1272. }
  1273. }
  1274. //-----------------------------------------------------------------------------
  1275. // Purpose: Sets the next hull to draw, or none if at end of hulls
  1276. //-----------------------------------------------------------------------------
  1277. void CAI_NetworkEditTools::DrawNextHull(const char *ainet_name)
  1278. {
  1279. m_iHullDrawNum++;
  1280. if (m_iHullDrawNum == NUM_HULLS)
  1281. {
  1282. m_iHullDrawNum = 0;
  1283. }
  1284. // Recalculate usable nodes for current hull
  1285. g_pAINetworkManager->GetEditOps()->RecalcUsableNodesForHull();
  1286. }
  1287. //-----------------------------------------------------------------------------
  1288. //-----------------------------------------------------------------------------
  1289. void CAI_NetworkEditTools::DrawHull(Hull_t eHull)
  1290. {
  1291. m_iHullDrawNum = eHull;
  1292. if (m_iHullDrawNum >= NUM_HULLS)
  1293. {
  1294. m_iHullDrawNum = 0;
  1295. }
  1296. // Recalculate usable nodes for current hull
  1297. g_pAINetworkManager->GetEditOps()->RecalcUsableNodesForHull();
  1298. }
  1299. //-----------------------------------------------------------------------------
  1300. // Purpose: Used just for debug display, to color nodes grey that the
  1301. // currently selected hull size can't use.
  1302. //-----------------------------------------------------------------------------
  1303. void CAI_NetworkEditTools::RecalcUsableNodesForHull(void)
  1304. {
  1305. // -----------------------------------------------------
  1306. // Use test hull to check hull sizes
  1307. // -----------------------------------------------------
  1308. CAI_TestHull *m_pTestHull = CAI_TestHull::GetTestHull();
  1309. m_pTestHull->GetNavigator()->SetNetwork( g_pBigAINet );
  1310. m_pTestHull->SetHullType((Hull_t)m_iHullDrawNum);
  1311. m_pTestHull->SetHullSizeNormal();
  1312. for (int node=0;node<m_pNetwork->NumNodes();node++)
  1313. {
  1314. if ( ( m_pNetwork->GetNode(node)->m_eNodeInfo & ( HullToBit( (Hull_t)m_iHullDrawNum ) << NODE_ENT_FLAGS_SHIFT ) ) ||
  1315. m_pTestHull->GetNavigator()->CanFitAtNode(node))
  1316. {
  1317. m_pNetwork->GetNode(node)->m_eNodeInfo &= ~bits_NODE_WONT_FIT_HULL;
  1318. }
  1319. else
  1320. {
  1321. m_pNetwork->GetNode(node)->m_eNodeInfo |= bits_NODE_WONT_FIT_HULL;
  1322. }
  1323. }
  1324. CAI_TestHull::ReturnTestHull();
  1325. }
  1326. //-----------------------------------------------------------------------------
  1327. // Purpose: Sets debug bits
  1328. //-----------------------------------------------------------------------------
  1329. void CAI_NetworkEditTools::SetDebugBits(const char *ainet_name,int debug_bit)
  1330. {
  1331. CAI_NetworkEditTools *pEditOps = g_pAINetworkManager->GetEditOps();
  1332. if ( !pEditOps )
  1333. return;
  1334. if (debug_bit & bits_debugOverlayNodes)
  1335. {
  1336. if (pEditOps->m_debugNetOverlays & bits_debugOverlayNodesLev2)
  1337. {
  1338. pEditOps->m_debugNetOverlays &= ~bits_debugOverlayNodes;
  1339. pEditOps->m_debugNetOverlays &= ~bits_debugOverlayNodesLev2;
  1340. }
  1341. else if (pEditOps->m_debugNetOverlays & bits_debugOverlayNodes)
  1342. {
  1343. pEditOps->m_debugNetOverlays |= bits_debugOverlayNodesLev2;
  1344. }
  1345. else
  1346. {
  1347. pEditOps->m_debugNetOverlays |= bits_debugOverlayNodes;
  1348. // Recalculate usable nodes for current hull
  1349. pEditOps->RecalcUsableNodesForHull();
  1350. }
  1351. }
  1352. else if (pEditOps->m_debugNetOverlays & debug_bit)
  1353. {
  1354. pEditOps->m_debugNetOverlays &= ~debug_bit;
  1355. }
  1356. else
  1357. {
  1358. pEditOps->m_debugNetOverlays |= debug_bit;
  1359. }
  1360. }
  1361. //-----------------------------------------------------------------------------
  1362. // Purpose: Draws edit display info on screen
  1363. //-----------------------------------------------------------------------------
  1364. void CAI_NetworkEditTools::DrawEditInfoOverlay(void)
  1365. {
  1366. hudtextparms_s tTextParam;
  1367. tTextParam.x = 0.8;
  1368. tTextParam.y = 0.8;
  1369. tTextParam.effect = 0;
  1370. tTextParam.r1 = 255;
  1371. tTextParam.g1 = 255;
  1372. tTextParam.b1 = 255;
  1373. tTextParam.a1 = 255;
  1374. tTextParam.r2 = 255;
  1375. tTextParam.g2 = 255;
  1376. tTextParam.b2 = 255;
  1377. tTextParam.a2 = 255;
  1378. tTextParam.fadeinTime = 0;
  1379. tTextParam.fadeoutTime = 0;
  1380. tTextParam.holdTime = 1;
  1381. tTextParam.fxTime = 0;
  1382. tTextParam.channel = 0;
  1383. char hullTypeTxt[50];
  1384. char nodeTypeTxt[50];
  1385. char editTypeTxt[50];
  1386. char outTxt[255];
  1387. Q_snprintf(hullTypeTxt,sizeof(hullTypeTxt)," %s",NAI_Hull::Name(m_iHullDrawNum));
  1388. Q_snprintf(outTxt,sizeof(outTxt),"Displaying:\n%s\n\n", hullTypeTxt);
  1389. if (engine->IsInEditMode())
  1390. {
  1391. char outTxt2[255];
  1392. Q_snprintf(nodeTypeTxt,sizeof(nodeTypeTxt)," %s (l)", m_bLinkEditMode ? "Links":"Nodes");
  1393. Q_snprintf(editTypeTxt,sizeof(editTypeTxt)," %s (m)", m_bAirEditMode ? "Air":"Ground");
  1394. Q_snprintf(outTxt2,sizeof(outTxt2),"Editing:\n%s\n%s", editTypeTxt,nodeTypeTxt);
  1395. Q_strncat(outTxt,outTxt2,sizeof(outTxt), COPY_ALL_CHARACTERS);
  1396. // Print in red if network needs rebuilding
  1397. if (m_debugNetOverlays & bits_debugNeedRebuild)
  1398. {
  1399. tTextParam.g1 = 0;
  1400. tTextParam.b1 = 0;
  1401. }
  1402. }
  1403. UTIL_HudMessageAll( tTextParam, outTxt );
  1404. }
  1405. //-----------------------------------------------------------------------------
  1406. // Purpose: Draws AINetwork on the screen
  1407. //-----------------------------------------------------------------------------
  1408. void CAI_NetworkEditTools::DrawAINetworkOverlay(void)
  1409. {
  1410. // ------------------------------------
  1411. // If network isn't loaded yet return
  1412. // ------------------------------------
  1413. if (!CAI_NetworkManager::NetworksLoaded())
  1414. {
  1415. return;
  1416. }
  1417. // ----------------------------------------------
  1418. // So we don't fill up the client message queue
  1419. // with node drawing messages, only send them
  1420. // in chuncks
  1421. // ----------------------------------------------
  1422. static int startDrawNode = 0;
  1423. static int endDrawNode = 0;
  1424. static float flDrawDuration;
  1425. endDrawNode = startDrawNode + 20;
  1426. flDrawDuration = 0.1 * (m_pNetwork->NumNodes()-1)/20;
  1427. if ( flDrawDuration < .1 )
  1428. flDrawDuration = .1;
  1429. if (endDrawNode > m_pNetwork->NumNodes())
  1430. {
  1431. endDrawNode = m_pNetwork->NumNodes();
  1432. }
  1433. // ---------------------
  1434. // Draw grid
  1435. // ---------------------
  1436. if (m_debugNetOverlays & bits_debugOverlayGrid)
  1437. {
  1438. // Trace a line to where player is looking
  1439. CBasePlayer* pPlayer = UTIL_PlayerByIndex(CBaseEntity::m_nDebugPlayer);
  1440. if (pPlayer)
  1441. {
  1442. Vector vForward;
  1443. Vector vSource = pPlayer->EyePosition();
  1444. pPlayer->EyeVectors( &vForward );
  1445. trace_t tr;
  1446. AI_TraceLine ( vSource, vSource + vForward * 2048, MASK_SOLID, pPlayer, COLLISION_GROUP_NONE, &tr);
  1447. float dotPr = DotProduct(Vector(0,0,1),tr.plane.normal);
  1448. if (tr.fraction != 1.0 && dotPr > 0.5)
  1449. {
  1450. NDebugOverlay::Grid( tr.endpos + Vector(0,0,1) );
  1451. }
  1452. }
  1453. }
  1454. // --------------------
  1455. CAI_Node **pAINode = m_pNetwork->AccessNodes();
  1456. // --------------------
  1457. // Draw the graph connectivity
  1458. // ---------------------
  1459. if (m_debugNetOverlays & bits_debugOverlayGraphConnect)
  1460. {
  1461. // ---------------------------------------------------
  1462. // If network needs rebuilding do so before display
  1463. // --------------------------------------------------
  1464. if (m_debugNetOverlays & bits_debugNeedRebuild)
  1465. {
  1466. m_pManager->RebuildNetworkGraph();
  1467. }
  1468. else if (m_iGConnectivityNode != NO_NODE)
  1469. {
  1470. for (int node=0;node<m_pNetwork->NumNodes();node++)
  1471. {
  1472. if ( m_pNetwork->IsConnected( m_iGConnectivityNode, node) )
  1473. {
  1474. Vector srcPos = pAINode[m_iGConnectivityNode]->GetPosition(m_iHullDrawNum);
  1475. Vector desPos = pAINode[node]->GetPosition(m_iHullDrawNum);
  1476. NDebugOverlay::Line(srcPos, desPos, 255,0,255, false,0);
  1477. }
  1478. }
  1479. }
  1480. }
  1481. // --------------------
  1482. // Draw the hulls
  1483. // ---------------------
  1484. if (m_debugNetOverlays & bits_debugOverlayHulls)
  1485. {
  1486. // ---------------------------------------------------
  1487. // If network needs rebuilding do so before display
  1488. // --------------------------------------------------
  1489. if (m_debugNetOverlays & bits_debugNeedRebuild)
  1490. {
  1491. m_pManager->RebuildNetworkGraph();
  1492. }
  1493. else
  1494. {
  1495. for (int node=startDrawNode;node<endDrawNode;node++)
  1496. {
  1497. for (int link=0;link<pAINode[node]->NumLinks();link++)
  1498. {
  1499. // Only draw link once
  1500. if (pAINode[node]->GetLinkByIndex(link)->DestNodeID(node) < node)
  1501. {
  1502. Vector srcPos = pAINode[pAINode[node]->GetLinkByIndex(link)->m_iSrcID]->GetPosition(m_iHullDrawNum);
  1503. Vector desPos = pAINode[pAINode[node]->GetLinkByIndex(link)->m_iDestID]->GetPosition(m_iHullDrawNum);
  1504. Vector direction = desPos - srcPos;
  1505. float length = VectorNormalize(direction);
  1506. Vector hullMins = NAI_Hull::Mins(m_iHullDrawNum);
  1507. Vector hullMaxs = NAI_Hull::Maxs(m_iHullDrawNum);
  1508. hullMaxs.x = length + hullMaxs.x;
  1509. if (pAINode[node]->GetLinkByIndex(link)->m_iAcceptedMoveTypes[m_iHullDrawNum] & bits_CAP_MOVE_FLY)
  1510. {
  1511. NDebugOverlay::BoxDirection(srcPos, hullMins, hullMaxs, direction, 100,255,255,20,flDrawDuration);
  1512. }
  1513. if (pAINode[node]->GetLinkByIndex(link)->m_iAcceptedMoveTypes[m_iHullDrawNum] & bits_CAP_MOVE_CLIMB)
  1514. {
  1515. // Display as a vertical slice up the climbing surface unless dismount node
  1516. if (pAINode[pAINode[node]->GetLinkByIndex(link)->m_iSrcID]->GetOrigin() != pAINode[pAINode[node]->GetLinkByIndex(link)->m_iDestID]->GetOrigin())
  1517. {
  1518. hullMaxs.x = hullMaxs.x - length;
  1519. if (srcPos.z < desPos.z)
  1520. {
  1521. hullMaxs.z = length + hullMaxs.z;
  1522. }
  1523. else
  1524. {
  1525. hullMins.z = hullMins.z - length;
  1526. }
  1527. direction = Vector(0,1,0);
  1528. }
  1529. NDebugOverlay::BoxDirection(srcPos, hullMins, hullMaxs, direction, 255,0,255,20,flDrawDuration);
  1530. }
  1531. if (pAINode[node]->GetLinkByIndex(link)->m_iAcceptedMoveTypes[m_iHullDrawNum] & bits_CAP_MOVE_GROUND)
  1532. {
  1533. NDebugOverlay::BoxDirection(srcPos, hullMins, hullMaxs, direction, 0,255,50,20,flDrawDuration);
  1534. }
  1535. else if (pAINode[node]->GetLinkByIndex(link)->m_iAcceptedMoveTypes[m_iHullDrawNum] & bits_CAP_MOVE_JUMP)
  1536. {
  1537. NDebugOverlay::BoxDirection(srcPos, hullMins, hullMaxs, direction, 0,0,255,20,flDrawDuration);
  1538. }
  1539. else if (pAINode[node]->GetLinkByIndex(link)->m_iAcceptedMoveTypes[m_iHullDrawNum] & bits_CAP_MOVE_CRAWL)
  1540. {
  1541. NDebugOverlay::Line( srcPos, desPos, 255, 255, 0, false, flDrawDuration );
  1542. }
  1543. }
  1544. }
  1545. }
  1546. }
  1547. }
  1548. // --------------------
  1549. // Draw the hints
  1550. // ---------------------
  1551. if (m_debugNetOverlays & bits_debugOverlayHints)
  1552. {
  1553. CAI_HintManager::DrawHintOverlays(flDrawDuration);
  1554. }
  1555. // -------------------------------
  1556. // Draw the nodes and connections
  1557. // -------------------------------
  1558. if (m_debugNetOverlays & (bits_debugOverlayNodes | bits_debugOverlayConnections))
  1559. {
  1560. for (int node=startDrawNode;node<endDrawNode;node++)
  1561. {
  1562. // This gets expensive, so see if the node is visible to the client
  1563. if (pAINode[node]->GetType() == NODE_DELETED)
  1564. continue;
  1565. // --------------------
  1566. // Draw the connections
  1567. // ---------------------
  1568. if (m_debugNetOverlays & bits_debugOverlayConnections)
  1569. {
  1570. // ---------------------------------------------------
  1571. // If network needs rebuilding do so before display
  1572. // --------------------------------------------------
  1573. if (m_debugNetOverlays & bits_debugNeedRebuild)
  1574. {
  1575. m_pManager->RebuildNetworkGraph();
  1576. }
  1577. else
  1578. {
  1579. for (int link=0;link<pAINode[node]->NumLinks();link++) {
  1580. // Only draw link once
  1581. CAI_Link *pAILink = pAINode[node]->GetLinkByIndex(link);
  1582. if ( pAILink->DestNodeID(node) >= node )
  1583. continue;
  1584. int srcID = pAILink->m_iSrcID;
  1585. int desID = pAILink->m_iDestID;
  1586. Vector srcPos = pAINode[srcID]->GetPosition(m_iHullDrawNum);
  1587. Vector desPos = pAINode[desID]->GetPosition(m_iHullDrawNum);
  1588. int srcType = pAINode[srcID]->GetType();
  1589. int desType = pAINode[desID]->GetType();
  1590. int linkInfo = pAILink->m_LinkInfo;
  1591. int moveTypes = pAILink->m_iAcceptedMoveTypes[m_iHullDrawNum];
  1592. bool IsDangerous = ( pAILink->m_nDangerCount > 0 );
  1593. bool bIsLastResort = ( pAILink->m_LinkInfo & bits_PREFER_AVOID ) != 0;
  1594. // when rendering, raise NODE_GROUND off the floor slighty as they seem to clip too much
  1595. if ( srcType == NODE_GROUND)
  1596. {
  1597. srcPos.z += 1.0;
  1598. }
  1599. if ( desType == NODE_GROUND)
  1600. {
  1601. desPos.z += 1.0;
  1602. }
  1603. unsigned char r, g, b;
  1604. // Draw in red if stale link
  1605. if (linkInfo & bits_LINK_STALE_SUGGESTED)
  1606. {
  1607. r = 255; g = 0; b = 0;
  1608. bIsLastResort = false;
  1609. }
  1610. // Draw in grey if link turned off
  1611. else if (linkInfo & bits_LINK_OFF)
  1612. {
  1613. r = 100; g = 100; b = 100;
  1614. bIsLastResort = false;
  1615. }
  1616. // Draw in yellow if link is dangerous
  1617. else if ( IsDangerous )
  1618. {
  1619. r = 192; g = 192; b = 0;
  1620. }
  1621. else if ((m_debugNetOverlays & bits_debugOverlayFlyConnections) && (moveTypes & bits_CAP_MOVE_FLY))
  1622. {
  1623. r = 100; g = 255; b = 255;
  1624. }
  1625. else if (moveTypes & bits_CAP_MOVE_CLIMB)
  1626. {
  1627. r = 255; g = 0; b = 255;
  1628. }
  1629. else if (moveTypes & bits_CAP_MOVE_GROUND)
  1630. {
  1631. r = 0; g = 255; b = 50;
  1632. }
  1633. else if ((m_debugNetOverlays & bits_debugOverlayCrawlConnections) && (moveTypes & bits_CAP_MOVE_CRAWL))
  1634. {
  1635. r = 255; g = 255; b = 255;
  1636. }
  1637. else if ((m_debugNetOverlays & bits_debugOverlayJumpConnections) && (moveTypes & bits_CAP_MOVE_JUMP) )
  1638. {
  1639. r = 0; g = 0; b = 255;
  1640. }
  1641. else
  1642. { // Dark red if this hull can't use
  1643. bool isFly = ( srcType == NODE_AIR || desType == NODE_AIR );
  1644. bool isJump = true;
  1645. for ( int i = HULL_HUMAN; i < NUM_HULLS; i++ )
  1646. {
  1647. if ( pAINode[node]->GetLinkByIndex(link)->m_iAcceptedMoveTypes[i] & ~bits_CAP_MOVE_JUMP )
  1648. {
  1649. isJump = false;
  1650. break;
  1651. }
  1652. }
  1653. bool isCrawl = true;
  1654. for ( int i = HULL_HUMAN; i < NUM_HULLS; i++ )
  1655. {
  1656. if ( pAINode[node]->GetLinkByIndex(link)->m_iAcceptedMoveTypes[i] & ~bits_CAP_MOVE_CRAWL )
  1657. {
  1658. isCrawl = false;
  1659. break;
  1660. }
  1661. }
  1662. if ( ( isFly && (m_debugNetOverlays & bits_debugOverlayFlyConnections) ) ||
  1663. ( isJump && (m_debugNetOverlays & bits_debugOverlayJumpConnections) ) ||
  1664. ( isCrawl && (m_debugNetOverlays & bits_debugOverlayCrawlConnections) ) ||
  1665. ( !isFly && !isJump && !isCrawl ) )
  1666. {
  1667. r = 100; g = 25; b = 25;
  1668. }
  1669. else
  1670. {
  1671. continue;
  1672. }
  1673. }
  1674. if ( bIsLastResort )
  1675. {
  1676. r /= 2; g /= 2; b /= 2;
  1677. }
  1678. NDebugOverlay::Line( srcPos, desPos, r, g, b, false, flDrawDuration );
  1679. }
  1680. }
  1681. }
  1682. if (m_debugNetOverlays & bits_debugOverlayNodes)
  1683. {
  1684. int r = 255;
  1685. int g = 0;
  1686. int b = 0;
  1687. // If checking visibility base color off of visibility info
  1688. if (m_debugNetOverlays & bits_debugOverlayVisibility &&
  1689. m_iVisibilityNode != NO_NODE)
  1690. {
  1691. // ---------------------------------------------------
  1692. // If network needs rebuilding do so before display
  1693. // --------------------------------------------------
  1694. if (m_debugNetOverlays & bits_debugNeedRebuild)
  1695. {
  1696. m_pManager->RebuildNetworkGraph();
  1697. }
  1698. }
  1699. // If checking graph connectivity base color off of connectivity info
  1700. if (m_debugNetOverlays & bits_debugOverlayGraphConnect &&
  1701. m_iGConnectivityNode != NO_NODE)
  1702. {
  1703. // ---------------------------------------------------
  1704. // If network needs rebuilding do so before display
  1705. // --------------------------------------------------
  1706. if (m_debugNetOverlays & bits_debugNeedRebuild)
  1707. {
  1708. m_pManager->RebuildNetworkGraph();
  1709. }
  1710. else if (m_pNetwork->IsConnected( m_iGConnectivityNode, node) )
  1711. {
  1712. r = 0;
  1713. g = 0;
  1714. b = 255;
  1715. }
  1716. }
  1717. // Otherwise base color off of node type
  1718. else
  1719. {
  1720. // If node is new and hasn't been rebuild yet
  1721. if (pAINode[node]->m_eNodeInfo & bits_NODE_WC_CHANGED)
  1722. {
  1723. r = 200;
  1724. g = 200;
  1725. b = 200;
  1726. }
  1727. // If node doesn't fit the current hull size
  1728. else if (pAINode[node]->m_eNodeInfo & bits_NODE_WONT_FIT_HULL)
  1729. {
  1730. r = 255;
  1731. g = 25;
  1732. b = 25;
  1733. }
  1734. else if (pAINode[node]->GetType() == NODE_CLIMB)
  1735. {
  1736. r = 255;
  1737. g = 0;
  1738. b = 255;
  1739. }
  1740. else if (pAINode[node]->GetType() == NODE_AIR)
  1741. {
  1742. r = 0;
  1743. g = 255;
  1744. b = 255;
  1745. }
  1746. else if (pAINode[node]->GetType() == NODE_GROUND)
  1747. {
  1748. r = 0;
  1749. g = 255;
  1750. b = 100;
  1751. }
  1752. }
  1753. Vector nodePos;
  1754. nodePos = pAINode[node]->GetPosition(m_iHullDrawNum);
  1755. NDebugOverlay::Box(nodePos, Vector(-5,-5,-5), Vector(5,5,5), r,g,b,0,flDrawDuration);
  1756. // If climb node draw line in facing direction
  1757. if (pAINode[node]->GetType() == NODE_CLIMB)
  1758. {
  1759. Vector offsetDir = 12.0 * Vector(cos(DEG2RAD(pAINode[node]->GetYaw())),sin(DEG2RAD(pAINode[node]->GetYaw())),flDrawDuration);
  1760. NDebugOverlay::Line(nodePos, nodePos+offsetDir, r,g,b,false,flDrawDuration);
  1761. }
  1762. if ( pAINode[node]->GetHint() )
  1763. {
  1764. NDebugOverlay::Box( nodePos, Vector(-7,-7,-7), Vector(7,7,7), 255,255,0,0,flDrawDuration);
  1765. }
  1766. if (m_debugNetOverlays & bits_debugOverlayNodesLev2)
  1767. {
  1768. CFmtStr msg;
  1769. if ( m_pNodeIndexTable )
  1770. msg.sprintf("%i (wc:%i; z:%i)",node,m_pNodeIndexTable[pAINode[node]->GetId()], pAINode[node]->GetZone());
  1771. else
  1772. msg.sprintf("%i (z:%i)",node,pAINode[node]->GetZone());
  1773. Vector loc = nodePos;
  1774. loc.x+=6;
  1775. loc.y+=6;
  1776. loc.z+=6;
  1777. NDebugOverlay::Text( loc, msg, true, flDrawDuration);
  1778. // Print the hintgroup if we have one
  1779. if ( pAINode[node]->GetHint() )
  1780. {
  1781. msg.sprintf("%s", STRING( pAINode[node]->GetHint()->GetGroup() ));
  1782. loc.z-=3;
  1783. NDebugOverlay::Text( loc, msg, true, flDrawDuration);
  1784. }
  1785. }
  1786. }
  1787. }
  1788. }
  1789. // -------------------------------
  1790. // Identify hull being displayed
  1791. // -------------------------------
  1792. if (m_debugNetOverlays & (bits_debugOverlayNodes | bits_debugOverlayConnections | bits_debugOverlayHulls))
  1793. {
  1794. DrawEditInfoOverlay();
  1795. }
  1796. // ----------------------------
  1797. // Increment node draw chunk
  1798. // ----------------------------
  1799. startDrawNode = endDrawNode;
  1800. if (startDrawNode >= m_pNetwork->NumNodes())
  1801. {
  1802. startDrawNode = 0;
  1803. }
  1804. // ----------------------------
  1805. // Output performance stats
  1806. // ----------------------------
  1807. #ifdef AI_PERF_MON
  1808. if (m_fNextPerfStatTime < gpGlobals->curtime)
  1809. {
  1810. char temp[512];
  1811. Q_snprintf(temp,sizeof(temp),"%3.2f NN/m\n%3.2f P/m\n",(m_nPerfStatNN/1.0),(m_nPerfStatPB/1.0));
  1812. UTIL_CenterPrintAll(temp);
  1813. m_fNextPerfStatTime = gpGlobals->curtime + 1;
  1814. m_nPerfStatNN = 0;
  1815. m_nPerfStatPB = 0;
  1816. }
  1817. #endif
  1818. }
  1819. //-----------------------------------------------------------------------------
  1820. // Purpose: Constructor
  1821. //-----------------------------------------------------------------------------
  1822. CAI_NetworkEditTools::CAI_NetworkEditTools(CAI_NetworkManager *pNetworkManager)
  1823. {
  1824. // ----------------------------------------------------------------------------
  1825. // If in wc_edit mode
  1826. // ----------------------------------------------------------------------------
  1827. if (engine->IsInEditMode())
  1828. {
  1829. // ----------------------------------------------------------------------------
  1830. // Allocate extra space for storing undropped node positions
  1831. // ----------------------------------------------------------------------------
  1832. m_pWCPosition = new Vector[MAX_NODES];
  1833. }
  1834. else
  1835. {
  1836. m_pWCPosition = NULL;
  1837. }
  1838. m_pNodeIndexTable = NULL;
  1839. m_debugNetOverlays = 0;
  1840. // ----------------------------------------------------------------------------
  1841. // Allocate table of WC Id's. If not in edit mode Deleted after initialization
  1842. // ----------------------------------------------------------------------------
  1843. m_pNodeIndexTable = new int[MAX_NODES];
  1844. for ( int i = 0; i < MAX_NODES; i++ )
  1845. m_pNodeIndexTable[i] = NO_NODE;
  1846. m_nNextWCIndex = 0;
  1847. m_pNetwork = pNetworkManager->GetNetwork(); // @tbd
  1848. m_pManager = pNetworkManager;
  1849. }
  1850. //-----------------------------------------------------------------------------
  1851. // Purpose: Destructor
  1852. //-----------------------------------------------------------------------------
  1853. CAI_NetworkEditTools::~CAI_NetworkEditTools()
  1854. {
  1855. // --------------------------------------------------------
  1856. // If in edit mode tell WC I'm ending my session
  1857. // --------------------------------------------------------
  1858. #ifdef _WIN32
  1859. Editor_EndSession(false);
  1860. #endif
  1861. delete[] m_pNodeIndexTable;
  1862. }
  1863. //-----------------------------------------------------------------------------
  1864. // CAI_NetworkBuilder
  1865. //
  1866. //-----------------------------------------------------------------------------
  1867. //-----------------------------------------------------------------------------
  1868. //-----------------------------------------------------------------------------
  1869. void CAI_NetworkBuilder::FloodFillZone( CAI_Node **ppNodes, CAI_Node *pNode, int zone )
  1870. {
  1871. pNode->SetZone( zone );
  1872. for (int link = 0; link < pNode->NumLinks(); link++)
  1873. {
  1874. CAI_Link *pLink = pNode->GetLinkByIndex(link);
  1875. CAI_Node *pLinkedNode = ( pLink->m_iDestID == pNode->GetId()) ? ppNodes[pLink->m_iSrcID] : ppNodes[pLink->m_iDestID];
  1876. if ( pLinkedNode->GetZone() == AI_NODE_ZONE_UNKNOWN )
  1877. FloodFillZone( ppNodes, pLinkedNode, zone );
  1878. Assert( pLinkedNode->GetZone() == pNode->GetZone() && pNode->GetZone() == zone );
  1879. }
  1880. }
  1881. //-----------------------------------------------------------------------------
  1882. //-----------------------------------------------------------------------------
  1883. void CAI_NetworkBuilder::InitZones( CAI_Network *pNetwork )
  1884. {
  1885. int nNodes = pNetwork->NumNodes();
  1886. CAI_Node **ppNodes = pNetwork->AccessNodes();
  1887. if ( !nNodes )
  1888. return;
  1889. int i;
  1890. for (i = 0; i < nNodes; i++)
  1891. {
  1892. ppNodes[i]->SetZone( AI_NODE_ZONE_UNKNOWN );
  1893. }
  1894. // Mark solo nodes
  1895. for (i = 0; i < nNodes; i++)
  1896. {
  1897. if ( ppNodes[i]->NumLinks() == 0 )
  1898. ppNodes[i]->SetZone( AI_NODE_ZONE_SOLO );
  1899. }
  1900. int curZone = AI_NODE_FIRST_ZONE;
  1901. for (i = 0; i < nNodes; i++)
  1902. {
  1903. if ( ppNodes[i]->GetZone() == AI_NODE_ZONE_UNKNOWN )
  1904. {
  1905. FloodFillZone( (CAI_Node **)ppNodes, ppNodes[i], curZone );
  1906. curZone++;
  1907. }
  1908. }
  1909. #ifdef DEBUG
  1910. for (i = 0; i < nNodes; i++)
  1911. {
  1912. Assert( ppNodes[i]->GetZone() != AI_NODE_ZONE_UNKNOWN );
  1913. }
  1914. #endif
  1915. }
  1916. //-----------------------------------------------------------------------------
  1917. // Purpose: Used for WC edit move to rebuild the network around the given
  1918. // location. Rebuilding the entire network takes too long
  1919. //-----------------------------------------------------------------------------
  1920. void CAI_NetworkBuilder::Rebuild( CAI_Network *pNetwork )
  1921. {
  1922. int nNodes = pNetwork->NumNodes();
  1923. CAI_Node **ppNodes = pNetwork->AccessNodes();
  1924. if ( !nNodes )
  1925. return;
  1926. BeginBuild();
  1927. // ------------------------------------------------------------
  1928. // First mark all nodes around vecPos as having to be rebuilt
  1929. // ------------------------------------------------------------
  1930. int i;
  1931. for (i = 0; i < nNodes; i++)
  1932. {
  1933. // --------------------------------------------------------------------
  1934. // If changed, mark all nodes that are within the max link distance to
  1935. // the changed node as having to be rebuild
  1936. // --------------------------------------------------------------------
  1937. if (ppNodes[i]->m_eNodeInfo & bits_NODE_WC_CHANGED)
  1938. {
  1939. Vector vRebuildPos = ppNodes[i]->GetOrigin();
  1940. ppNodes[i]->SetNeedsRebuild();
  1941. ppNodes[i]->SetZone( AI_NODE_ZONE_UNIVERSAL );
  1942. for (int node = 0; node < nNodes; node++)
  1943. {
  1944. if ( ppNodes[node]->GetType() == NODE_AIR )
  1945. {
  1946. if ((ppNodes[node]->GetOrigin() - vRebuildPos).LengthSqr() < MAX_AIR_NODE_LINK_DIST_SQ)
  1947. {
  1948. ppNodes[node]->SetNeedsRebuild();
  1949. ppNodes[node]->SetZone( AI_NODE_ZONE_UNIVERSAL );
  1950. }
  1951. }
  1952. else
  1953. {
  1954. if ((ppNodes[node]->GetOrigin() - vRebuildPos).LengthSqr() < MAX_NODE_LINK_DIST_SQ)
  1955. {
  1956. ppNodes[node]->SetNeedsRebuild();
  1957. ppNodes[node]->SetZone( AI_NODE_ZONE_UNIVERSAL );
  1958. }
  1959. }
  1960. }
  1961. }
  1962. }
  1963. // ---------------------------
  1964. // Initialize node positions
  1965. // ---------------------------
  1966. for (i = 0; i < nNodes; i++)
  1967. {
  1968. if (ppNodes[i]->NeedsRebuild())
  1969. {
  1970. InitNodePosition( pNetwork, ppNodes[i] );
  1971. }
  1972. }
  1973. nNodes = pNetwork->NumNodes(); // InitNodePosition can create nodes
  1974. // ---------------------------
  1975. // Initialize node neighbors
  1976. // ---------------------------
  1977. m_DidSetNeighborsTable.Resize( nNodes );
  1978. m_DidSetNeighborsTable.ClearAll();
  1979. m_NeighborsTable.SetSize( nNodes );
  1980. for (i = 0; i < nNodes; i++)
  1981. {
  1982. m_NeighborsTable[i].Resize( nNodes );
  1983. }
  1984. for (i = 0; i < nNodes; i++)
  1985. {
  1986. // If near point of change recalculate
  1987. if (ppNodes[i]->NeedsRebuild())
  1988. {
  1989. InitNeighbors( pNetwork, ppNodes[i] );
  1990. }
  1991. }
  1992. // ---------------------------
  1993. // Force node neighbors for dynamic links
  1994. // ---------------------------
  1995. ForceDynamicLinkNeighbors();
  1996. // ---------------------------
  1997. // Initialize accepted hulls
  1998. // ---------------------------
  1999. for (i = 0; i < nNodes; i++)
  2000. {
  2001. if (ppNodes[i]->NeedsRebuild())
  2002. {
  2003. ppNodes[i]->ClearLinks();
  2004. }
  2005. }
  2006. for (i = 0; i < nNodes; i++)
  2007. {
  2008. if (ppNodes[i]->NeedsRebuild())
  2009. {
  2010. InitLinks( pNetwork, ppNodes[i] );
  2011. }
  2012. }
  2013. g_pAINetworkManager->FixupHints();
  2014. EndBuild();
  2015. }
  2016. //-----------------------------------------------------------------------------
  2017. void CAI_NetworkBuilder::BeginBuild()
  2018. {
  2019. m_pTestHull = CAI_TestHull::GetTestHull();
  2020. }
  2021. //-----------------------------------------------------------------------------
  2022. void CAI_NetworkBuilder::EndBuild()
  2023. {
  2024. m_NeighborsTable.SetSize(0);
  2025. m_DidSetNeighborsTable.Resize(0);
  2026. CAI_TestHull::ReturnTestHull();
  2027. }
  2028. //-----------------------------------------------------------------------------
  2029. // Purpose: Only called if network has changed since last time level
  2030. // was loaded
  2031. //-----------------------------------------------------------------------------
  2032. void CAI_NetworkBuilder::Build( CAI_Network *pNetwork )
  2033. {
  2034. int nNodes = pNetwork->NumNodes();
  2035. CAI_Node **ppNodes = pNetwork->AccessNodes();
  2036. if ( !nNodes )
  2037. return;
  2038. CAI_NetworkBuildHelper *pHelper = (CAI_NetworkBuildHelper *)CreateEntityByName( "ai_network_build_helper" );
  2039. VPROF( "AINet" );
  2040. BeginBuild();
  2041. CFastTimer masterTimer;
  2042. CFastTimer timer;
  2043. DevMsg( "Building AI node graph...\n");
  2044. masterTimer.Start();
  2045. // ---------------------------
  2046. // Initialize node positions
  2047. // ---------------------------
  2048. DevMsg( "Initializing node positions...\n" );
  2049. timer.Start();
  2050. int i;
  2051. for ( i = 0; i < nNodes; i++)
  2052. {
  2053. InitNodePosition( pNetwork, ppNodes[i] );
  2054. if ( pHelper )
  2055. pHelper->PostInitNodePosition( pNetwork, ppNodes[i] );
  2056. }
  2057. nNodes = pNetwork->NumNodes(); // InitNodePosition can create nodes
  2058. timer.End();
  2059. DevMsg( "...done initializing node positions. %f seconds\n", timer.GetDuration().GetSeconds() );
  2060. // ---------------------------
  2061. // Initialize node neighbors
  2062. // ---------------------------
  2063. DevMsg( "Initializing node neighbors...\n" );
  2064. timer.Start();
  2065. m_DidSetNeighborsTable.Resize( nNodes );
  2066. m_DidSetNeighborsTable.ClearAll();
  2067. m_NeighborsTable.SetSize( nNodes );
  2068. for (i = 0; i < nNodes; i++)
  2069. {
  2070. m_NeighborsTable[i].Resize( nNodes );
  2071. m_NeighborsTable[i].ClearAll();
  2072. }
  2073. for (i = 0; i < nNodes; i++)
  2074. {
  2075. InitNeighbors( pNetwork, ppNodes[i] );
  2076. }
  2077. timer.End();
  2078. DevMsg( "...done initializing node neighbors. %f seconds\n", timer.GetDuration().GetSeconds() );
  2079. // ---------------------------
  2080. // Force node neighbors for dynamic links
  2081. // ---------------------------
  2082. DevMsg( "Forcing dynamic link neighbors...\n" );
  2083. timer.Start();
  2084. ForceDynamicLinkNeighbors();
  2085. timer.End();
  2086. DevMsg( "...done forcing dynamic link neighbors. %f seconds\n", timer.GetDuration().GetSeconds() );
  2087. // ---------------------------
  2088. // Initialize accepted hulls
  2089. // ---------------------------
  2090. DevMsg( "Determining links...\n" );
  2091. timer.Start();
  2092. for (i = 0; i < nNodes; i++)
  2093. {
  2094. // Make sure all the links are clear
  2095. ppNodes[i]->ClearLinks();
  2096. }
  2097. for (i = 0; i < nNodes; i++)
  2098. {
  2099. InitLinks( pNetwork, ppNodes[i] );
  2100. }
  2101. timer.End();
  2102. DevMsg( "...done determining links. %f seconds\n", timer.GetDuration().GetSeconds() );
  2103. // ------------------------------
  2104. // Initialize disconnected nodes
  2105. // ------------------------------
  2106. DevMsg( "Determining zones...\n" );
  2107. timer.Start();
  2108. InitZones( pNetwork);
  2109. timer.End();
  2110. masterTimer.End();
  2111. DevMsg( "...done determining zones. %f seconds\n", timer.GetDuration().GetSeconds() );
  2112. DevMsg( "...done building AI node graph, %f seconds\n", masterTimer.GetDuration().GetSeconds() );
  2113. g_pAINetworkManager->FixupHints();
  2114. EndBuild();
  2115. if ( pHelper )
  2116. UTIL_Remove( pHelper );
  2117. }
  2118. //------------------------------------------------------------------------------
  2119. // Purpose : Forces testing of a connection between src and dest IDs for all dynamic links
  2120. //
  2121. // Input :
  2122. // Output :
  2123. //------------------------------------------------------------------------------
  2124. void CAI_NetworkBuilder::ForceDynamicLinkNeighbors(void)
  2125. {
  2126. if (!g_pAINetworkManager->GetEditOps()->m_pNodeIndexTable)
  2127. {
  2128. DevMsg("ERROR: Trying initialize links with no WC ID table!\n");
  2129. return;
  2130. }
  2131. CAI_DynamicLink* pDynamicLink = CAI_DynamicLink::m_pAllDynamicLinks;
  2132. while (pDynamicLink)
  2133. {
  2134. // -------------------------------------------------------------
  2135. // First convert this links WC IDs to engine IDs
  2136. // -------------------------------------------------------------
  2137. int nSrcID = g_pAINetworkManager->GetEditOps()->GetNodeIdFromWCId( pDynamicLink->m_nSrcEditID );
  2138. if (nSrcID == -1)
  2139. {
  2140. DevMsg("ERROR: Dynamic link source WC node %d not found\n", pDynamicLink->m_nSrcEditID );
  2141. }
  2142. int nDestID = g_pAINetworkManager->GetEditOps()->GetNodeIdFromWCId( pDynamicLink->m_nDestEditID );
  2143. if (nDestID == -1)
  2144. {
  2145. DevMsg("ERROR: Dynamic link dest WC node %d not found\n", pDynamicLink->m_nDestEditID );
  2146. }
  2147. if ( nSrcID != -1 && nDestID != -1 )
  2148. {
  2149. if ( nSrcID < g_pBigAINet->NumNodes() && nDestID < g_pBigAINet->NumNodes() )
  2150. {
  2151. CAI_Node *pSrcNode = g_pBigAINet->GetNode( nSrcID );
  2152. CAI_Node *pDestNode = g_pBigAINet->GetNode( nDestID );
  2153. // -------------------------------------------------------------
  2154. // Force visibility and neighbor-ness between the nodes
  2155. // -------------------------------------------------------------
  2156. Assert( pSrcNode );
  2157. Assert( pDestNode );
  2158. m_NeighborsTable[pSrcNode->GetId()].Set(pDestNode->GetId());
  2159. m_NeighborsTable[pDestNode->GetId()].Set(pSrcNode->GetId());
  2160. }
  2161. }
  2162. // Go on to the next dynamic link
  2163. pDynamicLink = pDynamicLink->m_pNextDynamicLink;
  2164. }
  2165. }
  2166. CAI_NetworkBuilder g_AINetworkBuilder;
  2167. //-----------------------------------------------------------------------------
  2168. // Purpose: Initializes position of climb node in the world. Climb nodes are
  2169. // set to be just above the floor or at the same level at the
  2170. // dismount point for the node
  2171. //
  2172. // Input :
  2173. // Output :
  2174. //-----------------------------------------------------------------------------
  2175. void CAI_NetworkBuilder::InitClimbNodePosition(CAI_Network *pNetwork, CAI_Node *pNode)
  2176. {
  2177. AI_PROFILE_SCOPE( CAI_Node_InitClimbNodePosition );
  2178. // If this is a node for mounting/dismounting the climb skip it
  2179. if ( pNode->m_eNodeInfo & (bits_NODE_CLIMB_OFF_FORWARD | bits_NODE_CLIMB_OFF_LEFT | bits_NODE_CLIMB_OFF_RIGHT) )
  2180. {
  2181. return;
  2182. }
  2183. // Figure out which directions I can dismount from the climb node
  2184. //float hullLength = NAI_Hull::Length(HULL_SMALL);
  2185. //Vector offsetDir = Vector(cos(DEG2RAD(m_flYaw)),sin(DEG2RAD(m_flYaw)),0);
  2186. // ----------------
  2187. // Check position
  2188. // ----------------
  2189. trace_t trace;
  2190. Vector posOnLadder = pNode->GetPosition(HULL_SMALL_CENTERED);
  2191. AI_TraceHull( posOnLadder, posOnLadder + Vector( 0, 0, -37 ),
  2192. NAI_Hull::Mins(HULL_SMALL_CENTERED), NAI_Hull::Maxs(HULL_SMALL_CENTERED),
  2193. MASK_NPCSOLID_BRUSHONLY, NULL, COLLISION_GROUP_NONE, &trace );
  2194. // --------------------------------------------------------------------
  2195. // If climb node is right above the floor, we don't need any dismount
  2196. // nodes. Accept this dropped position and note that this climb node
  2197. // is at the bottom
  2198. // --------------------------------------------------------------------
  2199. if (!trace.startsolid && trace.fraction != 1)
  2200. {
  2201. pNode->m_eNodeInfo = bits_NODE_CLIMB_BOTTOM;
  2202. InitGroundNodePosition( pNetwork, pNode );
  2203. return;
  2204. }
  2205. // ---------------------------------------------------------------------
  2206. // If network was already loaded this means we are in wc edit mode
  2207. // so we shouldn't recreate the added climb nodes
  2208. // ---------------------------------------------------------------------
  2209. if (g_pAINetworkManager->NetworksLoaded())
  2210. {
  2211. return;
  2212. }
  2213. // ---------------------------------------------------------------------
  2214. // Otherwise we need to create climb nodes for dismounting the climb
  2215. // and place the height of the climb node at the dismount position
  2216. // ---------------------------------------------------------------------
  2217. int checkNodeTypes[3] = { bits_NODE_CLIMB_OFF_FORWARD, bits_NODE_CLIMB_OFF_LEFT, bits_NODE_CLIMB_OFF_RIGHT };
  2218. int numExits = 0;
  2219. // DevMsg( "testing %f %f %f\n", GetOrigin().x, GetOrigin().y, GetOrigin().z );
  2220. for (int i = 0; i < 3; i++)
  2221. {
  2222. pNode->m_eNodeInfo = checkNodeTypes[i];
  2223. Vector origin = pNode->GetPosition(HULL_SMALL_CENTERED);
  2224. // DevMsg( "testing %f %f %f\n", origin.x, origin.y, origin.z );
  2225. // ----------------
  2226. // Check outward
  2227. // ----------------
  2228. AI_TraceLine ( posOnLadder,
  2229. origin,
  2230. MASK_NPCSOLID_BRUSHONLY,
  2231. NULL,
  2232. COLLISION_GROUP_NONE,
  2233. &trace );
  2234. // DevMsg( "to %f %f %f : %d %f", origin.x, origin.y, origin.z, trace.startsolid, trace.fraction );
  2235. if (!trace.startsolid && trace.fraction == 1.0)
  2236. {
  2237. float floorZ = GetFloorZ(origin); // FIXME: don't use this
  2238. if (abs(pNode->GetOrigin().z - floorZ) < 36)
  2239. {
  2240. CAI_Node *new_node = pNetwork->AddNode( pNode->GetOrigin(), pNode->m_flYaw );
  2241. new_node->m_pHint = NULL;
  2242. new_node->m_eNodeType = NODE_CLIMB;
  2243. new_node->m_eNodeInfo = pNode->m_eNodeInfo;
  2244. InitGroundNodePosition( pNetwork, new_node );
  2245. // copy over the offsets for the first CLIMB_OFF node
  2246. // FIXME: this method is broken for when the CLIMB_OFF nodes are at different heights
  2247. if (numExits == 0)
  2248. {
  2249. for (int hull = 0; hull < NUM_HULLS; hull++)
  2250. {
  2251. pNode->m_flVOffset[hull] = new_node->m_flVOffset[hull];
  2252. }
  2253. }
  2254. else
  2255. {
  2256. for (int hull = 0; hull < NUM_HULLS; hull++)
  2257. {
  2258. if (fabs(pNode->m_flVOffset[hull] - new_node->m_flVOffset[hull]) > 1)
  2259. {
  2260. DevMsg(2, "Warning: Climb Node %i has different exit heights for hull %s\n", pNode->m_iID, NAI_Hull::Name(hull));
  2261. }
  2262. }
  2263. }
  2264. numExits++;
  2265. }
  2266. }
  2267. // DevMsg( "\n");
  2268. }
  2269. if (numExits == 0)
  2270. {
  2271. DevMsg("ERROR: Climb Node %i has no way off\n",pNode->m_iID);
  2272. }
  2273. // this is a node that can't get gotten to directly
  2274. pNode->m_eNodeInfo = bits_NODE_CLIMB_ON;
  2275. }
  2276. //-----------------------------------------------------------------------------
  2277. // Purpose: Initializes position of the node sitting on the ground.
  2278. // Input :
  2279. // Output :
  2280. //-----------------------------------------------------------------------------
  2281. void CAI_NetworkBuilder::InitGroundNodePosition(CAI_Network *pNetwork, CAI_Node *pNode)
  2282. {
  2283. AI_PROFILE_SCOPE( CAI_Node_InitGroundNodePosition );
  2284. if ( pNode->m_eNodeInfo & bits_DONT_DROP )
  2285. return;
  2286. // find actual floor for each hull type
  2287. for (int hull = 0; hull < NUM_HULLS; hull++)
  2288. {
  2289. trace_t tr;
  2290. Vector origin = pNode->GetOrigin();
  2291. Vector mins, maxs;
  2292. // turn hull into pancake to avoid problems with ceiling
  2293. mins = NAI_Hull::Mins(hull);
  2294. maxs = NAI_Hull::Maxs(hull);
  2295. maxs.z = mins.z;
  2296. // Add an epsilon for cast
  2297. origin.z += 0.1;
  2298. // shift up so bottom of box is at center of node
  2299. origin.z -= mins.z;
  2300. AI_TraceHull( origin, origin + Vector( 0, 0, -384 ), mins, maxs, MASK_NPCSOLID_BRUSHONLY, NULL, COLLISION_GROUP_NONE, &tr );
  2301. if ( !tr.startsolid )
  2302. pNode->m_flVOffset[hull] = tr.endpos.z - pNode->GetOrigin().z + 0.1;
  2303. else
  2304. pNode->m_flVOffset[hull] = -mins.z + 0.1;
  2305. }
  2306. }
  2307. //-----------------------------------------------------------------------------
  2308. // Purpose: Initializes position of the node in the world. Only called if
  2309. // the network was never initialized
  2310. // Input :
  2311. // Output :
  2312. //-----------------------------------------------------------------------------
  2313. void CAI_NetworkBuilder::InitNodePosition(CAI_Network *pNetwork, CAI_Node *pNode)
  2314. {
  2315. AI_PROFILE_SCOPE( CAI_Node_InitNodePosition );
  2316. if (pNode->m_eNodeType == NODE_AIR)
  2317. {
  2318. return;
  2319. }
  2320. else if (pNode->m_eNodeType == NODE_CLIMB)
  2321. {
  2322. InitClimbNodePosition(pNetwork, pNode);
  2323. return;
  2324. }
  2325. // Otherwise mark as a land node and drop to the floor
  2326. else if (pNode->m_eNodeType == NODE_GROUND)
  2327. {
  2328. InitGroundNodePosition( pNetwork, pNode );
  2329. if (pNode->m_flVOffset[HULL_SMALL_CENTERED] < -100)
  2330. {
  2331. Assert( pNetwork == g_pBigAINet );
  2332. DevWarning("ERROR: Node %.0f %.0f %.0f, WC ID# %i, is either too low (fell through floor) or too high (>100 units above floor)\n",
  2333. pNode->GetOrigin().x, pNode->GetOrigin().y, pNode->GetOrigin().z,
  2334. g_pAINetworkManager->GetEditOps()->m_pNodeIndexTable[pNode->m_iID]);
  2335. pNode->m_eNodeInfo |= bits_NODE_FALLEN;
  2336. }
  2337. return;
  2338. }
  2339. /* // If under water, not that the node is in water <<TODO>> when we get water
  2340. else if ( UTIL_PointContents(GetOrigin()) & MASK_WATER )
  2341. {
  2342. m_eNodeType |= NODE_WATER;
  2343. }
  2344. */
  2345. else if (pNode->m_eNodeType != NODE_DELETED)
  2346. {
  2347. DevMsg( "Bad node type!\n" );
  2348. }
  2349. }
  2350. //-----------------------------------------------------------------------------
  2351. // Purpose: Set the visibility for this node. (What nodes it can see with a
  2352. // line trace)
  2353. // Input :
  2354. // Output :
  2355. //-----------------------------------------------------------------------------
  2356. void CAI_NetworkBuilder::InitVisibility(CAI_Network *pNetwork, CAI_Node *pNode)
  2357. {
  2358. AI_PROFILE_SCOPE( CAI_Node_InitVisibility );
  2359. // If a deleted node bail
  2360. if (pNode->m_eNodeType == NODE_DELETED)
  2361. {
  2362. return;
  2363. }
  2364. // The actual position of some nodes may be inside geometry as they have
  2365. // hull specific position offsets (e.g. climb nodes). Get the hull specific
  2366. // position using the smallest hull to make sure were not in geometry
  2367. Vector srcPos = pNode->GetPosition(HULL_SMALL_CENTERED);
  2368. // Check the visibility on every other node in the network
  2369. for (int testnode = 0; testnode < pNetwork->NumNodes(); testnode++ )
  2370. {
  2371. CAI_Node *testNode = pNetwork->GetNode( testnode );
  2372. if ( DebuggingConnect( pNode->m_iID, testnode ) )
  2373. {
  2374. DevMsg( "" ); // break here..
  2375. }
  2376. // We know we can view ourself
  2377. if (pNode->m_iID == testnode)
  2378. {
  2379. m_NeighborsTable[pNode->m_iID].Set(testNode->m_iID);
  2380. continue;
  2381. }
  2382. // Remove duplicate nodes unless a climb node as they move
  2383. if (testNode->GetOrigin() == pNode->GetOrigin() && testNode->GetType() != NODE_CLIMB)
  2384. {
  2385. testNode->SetType( NODE_DELETED );
  2386. DevMsg( 2, "Probable duplicate node placed at %s\n", VecToString(testNode->GetOrigin()) );
  2387. continue;
  2388. }
  2389. // If a deleted node we don't care about it
  2390. if (testNode->GetType() == NODE_DELETED)
  2391. {
  2392. continue;
  2393. }
  2394. if ( m_DidSetNeighborsTable.IsBitSet( testNode->m_iID ) )
  2395. {
  2396. if ( m_NeighborsTable[testNode->m_iID].IsBitSet(pNode->m_iID))
  2397. m_NeighborsTable[pNode->m_iID].Set(testNode->m_iID);
  2398. continue;
  2399. }
  2400. float flDistToCheckNode = ( testNode->GetOrigin() - pNode->GetOrigin() ).LengthSqr();
  2401. if ( testNode->GetType() == NODE_AIR )
  2402. {
  2403. if (flDistToCheckNode > MAX_AIR_NODE_LINK_DIST_SQ)
  2404. continue;
  2405. }
  2406. else
  2407. {
  2408. if (flDistToCheckNode > MAX_NODE_LINK_DIST_SQ)
  2409. continue;
  2410. }
  2411. // The actual position of some nodes may be inside geometry as they have
  2412. // hull specific position offsets (e.g. climb nodes). Get the hull specific
  2413. // position using the smallest hull to make sure were not in geometry
  2414. Vector destPos = pNetwork->GetNode( testnode )->GetPosition(HULL_SMALL_CENTERED);
  2415. trace_t tr;
  2416. tr.m_pEnt = NULL;
  2417. // Try several line of sight checks
  2418. bool isVisible = false;
  2419. // ------------------
  2420. // Bottom to bottom
  2421. // ------------------
  2422. AI_TraceLine ( srcPos, destPos,MASK_NPCWORLDSTATIC_FLUID,NULL,COLLISION_GROUP_NONE, &tr );
  2423. if (!tr.startsolid && tr.fraction == 1.0)
  2424. {
  2425. isVisible = true;
  2426. }
  2427. // ------------------
  2428. // Top to top
  2429. // ------------------
  2430. if (!isVisible)
  2431. {
  2432. AI_TraceLine ( srcPos + Vector( 0, 0, 70 ),destPos + Vector( 0, 0, 70 ),MASK_NPCWORLDSTATIC_FLUID,NULL,COLLISION_GROUP_NONE, &tr );
  2433. if (!tr.startsolid && tr.fraction == 1.0)
  2434. {
  2435. isVisible = true;
  2436. }
  2437. }
  2438. // ------------------
  2439. // Top to Bottom
  2440. // ------------------
  2441. if (!isVisible)
  2442. {
  2443. AI_TraceLine ( srcPos + Vector( 0, 0, 70 ),destPos,MASK_NPCWORLDSTATIC_FLUID,NULL,COLLISION_GROUP_NONE, &tr );
  2444. if (!tr.startsolid && tr.fraction == 1.0)
  2445. {
  2446. isVisible = true;
  2447. }
  2448. }
  2449. // ------------------
  2450. // Bottom to Top
  2451. // ------------------
  2452. if (!isVisible)
  2453. {
  2454. AI_TraceLine ( srcPos,destPos + Vector( 0, 0, 70 ),MASK_NPCWORLDSTATIC_FLUID,NULL,COLLISION_GROUP_NONE, &tr );
  2455. if (!tr.startsolid && tr.fraction == 1.0)
  2456. {
  2457. isVisible = true;
  2458. }
  2459. }
  2460. // ------------------
  2461. // Failure
  2462. // ------------------
  2463. if (!isVisible)
  2464. {
  2465. continue;
  2466. }
  2467. /* <<TODO>> may not apply with editable connections.......
  2468. // trace hit a brush ent, trace backwards to make sure that this ent is the only thing in the way.
  2469. if ( tr.fraction != 1.0 )
  2470. {
  2471. pTraceEnt = tr.u.ent;// store the ent that the trace hit, for comparison
  2472. AI_TraceLine ( srcPos,
  2473. destPos,
  2474. GetAITraceMask_BrushOnly(),
  2475. NULL,
  2476. &tr );
  2477. // there is a solid_bsp ent in the way of these two nodes, so we must record several things about in order to keep
  2478. // track of it in the pathfinding code, as well as through save and restore of the node graph. ANY data that is manipulated
  2479. // as part of the process of adding a LINKENT to a connection here must also be done in CGraph::SetGraphPointers, where reloaded
  2480. // graphs are prepared for use.
  2481. if ( tr.u.ent == pTraceEnt && !FClassnameIs( tr.u.ent, "worldspawn" ) )
  2482. {
  2483. // get a pointer
  2484. pLinkPool [ cTotalLinks ].m_pLinkEnt = tr.u.ent;
  2485. // record the modelname, so that we can save/load node trees
  2486. memcpy( pLinkPool [ cTotalLinks ].m_szLinkEntModelname, STRING( tr.u.ent->model ), 4 );
  2487. // set the flag for this ent that indicates that it is attached to the world graph
  2488. // if this ent is removed from the world, it must also be removed from the connections
  2489. // that it formerly blocked.
  2490. CBaseEntity *e = CBaseEntity::Instance( tr.u.ent );
  2491. if ( e )
  2492. {
  2493. if ( !(e->GetFlags() & FL_GRAPHED ) )
  2494. {
  2495. e->AddFlag( FL_GRAPHED );
  2496. }
  2497. }
  2498. }
  2499. // even if the ent wasn't there, these nodes couldn't be connected. Skip.
  2500. else
  2501. {
  2502. continue;
  2503. }
  2504. }
  2505. */
  2506. m_NeighborsTable[pNode->m_iID].Set(testNode->m_iID);
  2507. }
  2508. }
  2509. //-----------------------------------------------------------------------------
  2510. // Purpose: Initializes the neighbors list
  2511. // Input :
  2512. // Output :
  2513. //-----------------------------------------------------------------------------
  2514. void CAI_NetworkBuilder::InitNeighbors(CAI_Network *pNetwork, CAI_Node *pNode)
  2515. {
  2516. m_NeighborsTable[pNode->m_iID].ClearAll();
  2517. // Begin by establishing viewability to limit the number of nodes tested
  2518. InitVisibility( pNetwork, pNode );
  2519. AI_PROFILE_SCOPE_BEGIN( CAI_Node_InitNeighbors );
  2520. // Now check each neighbor against all other neighbors to see if one of
  2521. // them is a redundant connection
  2522. for (int checknode = 0; checknode < pNetwork->NumNodes(); checknode++ )
  2523. {
  2524. if ( DebuggingConnect( pNode->m_iID, checknode ) )
  2525. {
  2526. DevMsg( "" ); // break here..
  2527. }
  2528. // I'm not a neighbor of myself
  2529. if ( pNode->m_iID == checknode )
  2530. {
  2531. m_NeighborsTable[pNode->m_iID].Clear(checknode);
  2532. continue;
  2533. }
  2534. // Only check if already on the neightbor list
  2535. if (!m_NeighborsTable[pNode->m_iID].IsBitSet(checknode))
  2536. {
  2537. continue;
  2538. }
  2539. CAI_Node *pCheckNode = pNetwork->GetNode(checknode);
  2540. for (int testnode = 0; testnode < pNetwork->NumNodes(); testnode++ )
  2541. {
  2542. // don't check against itself
  2543. if (( testnode == checknode ) || (testnode == pNode->m_iID))
  2544. {
  2545. continue;
  2546. }
  2547. // Only check if already on the neightbor list
  2548. if (!m_NeighborsTable[pNode->m_iID].IsBitSet(testnode))
  2549. {
  2550. continue;
  2551. }
  2552. CAI_Node *pTestNode = pNetwork->GetNode(testnode);
  2553. // ----------------------------------------------------------
  2554. // Don't check air nodes against nodes of a different types
  2555. // ----------------------------------------------------------
  2556. if ((pCheckNode->GetType() == NODE_AIR && pTestNode->GetType() != NODE_AIR)||
  2557. (pCheckNode->GetType() != NODE_AIR && pTestNode->GetType() == NODE_AIR))
  2558. {
  2559. continue;
  2560. }
  2561. // ----------------------------------------------------------
  2562. // If climb node pairs, don't consider redundancy
  2563. // ----------------------------------------------------------
  2564. if (pNode->GetType() == NODE_CLIMB &&
  2565. (pCheckNode->GetType() == NODE_CLIMB || pTestNode->GetType() == NODE_CLIMB))
  2566. {
  2567. continue;
  2568. }
  2569. // ----------------------------------------------------------
  2570. // If a climb node mounting point is involved, don't consider redundancy
  2571. // ----------------------------------------------------------
  2572. if ( ( pCheckNode->GetOrigin() == pNode->GetOrigin() && pNode->GetType() == NODE_CLIMB && pCheckNode->GetType() == NODE_CLIMB ) ||
  2573. ( pTestNode->GetOrigin() == pNode->GetOrigin() && pNode->GetType() == NODE_CLIMB && pTestNode->GetType() == NODE_CLIMB ) ||
  2574. ( pTestNode->GetOrigin() == pCheckNode->GetOrigin() && pCheckNode->GetType() == NODE_CLIMB && pTestNode->GetType() == NODE_CLIMB ) )
  2575. {
  2576. continue;
  2577. }
  2578. // @HACKHACK (toml 02-25-04): Ignore redundancy if both nodes are air nodes with
  2579. // hint type "strider node". Really, really should do this in a clean manner
  2580. bool nodeIsStrider = ( pNode->GetHint() && pNode->GetHint()->HintType() == HINT_STRIDER_NODE );
  2581. bool other1IsStrider = ( pCheckNode->GetHint() && pCheckNode->GetHint()->HintType() == HINT_STRIDER_NODE );
  2582. bool other2IsStrider = ( pTestNode->GetHint() && pTestNode->GetHint()->HintType() == HINT_STRIDER_NODE );
  2583. if ( nodeIsStrider && other1IsStrider != other2IsStrider )
  2584. {
  2585. continue;
  2586. }
  2587. Vector vec2DirToCheckNode = pCheckNode->GetOrigin() - pNode->GetOrigin();
  2588. float flDistToCheckNode = VectorNormalize( vec2DirToCheckNode );
  2589. Vector vec2DirToTestNode = ( pTestNode->GetOrigin() - pNode->GetOrigin() );
  2590. float flDistToTestNode = VectorNormalize( vec2DirToTestNode );
  2591. float tolerance = 0.92388; // 45 degrees
  2592. if ( DotProduct ( vec2DirToCheckNode, vec2DirToTestNode ) >= tolerance )
  2593. {
  2594. if ( flDistToTestNode < flDistToCheckNode )
  2595. {
  2596. DebugConnectMsg( pNode->m_iID, checknode, " Revoking neighbor status to closer redundant link %d\n", testnode );
  2597. m_NeighborsTable[pNode->m_iID].Clear(checknode);
  2598. }
  2599. else
  2600. {
  2601. DebugConnectMsg( pNode->m_iID, testnode, " Revoking neighbor status to closer redundant link %d\n", checknode );
  2602. m_NeighborsTable[pNode->m_iID].Clear(testnode);
  2603. }
  2604. }
  2605. }
  2606. }
  2607. AI_PROFILE_SCOPE_END();
  2608. m_DidSetNeighborsTable.Set(pNode->m_iID);
  2609. }
  2610. //-----------------------------------------------------------------------------
  2611. // Purpose: For the current node, check its connection to all other nodes
  2612. // Input :
  2613. // Output :
  2614. //-----------------------------------------------------------------------------
  2615. static bool IsInLineForClimb( const Vector &srcPos, const Vector &srcFacing, const Vector &destPos, const Vector &destFacing )
  2616. {
  2617. #ifdef DEBUG
  2618. Vector normSrcFacing( srcFacing ), normDestFacing( destFacing );
  2619. VectorNormalize( normSrcFacing );
  2620. VectorNormalize( normDestFacing );
  2621. Assert( VectorsAreEqual( srcFacing, normSrcFacing, 0.01 ) && VectorsAreEqual( destFacing, normDestFacing, 0.01 ) );
  2622. #endif
  2623. // If they are not facing the same way...
  2624. if ( 1 - srcFacing.Dot( destFacing ) > 0.01 )
  2625. return false;
  2626. // If they aren't in line along the facing...
  2627. if ( CalcDistanceToLine2D( destPos.AsVector2D(), srcPos.AsVector2D(), srcPos.AsVector2D() + srcFacing.AsVector2D() ) > 0.01 )
  2628. return false;
  2629. // Check that the angle between them is either staight up, or on at angle of ladder-stairs
  2630. Vector vecDelta = srcPos - destPos;
  2631. VectorNormalize( vecDelta );
  2632. float fabsCos = fabs( srcFacing.Dot( vecDelta ) );
  2633. const float CosAngLadderStairs = 0.4472; // rise 2 & run 1
  2634. if ( fabsCos > 0.05 && fabs( fabsCos - CosAngLadderStairs ) > 0.05 )
  2635. return false;
  2636. // *************************** --------------------------------
  2637. return true;
  2638. }
  2639. //-------------------------------------
  2640. int CAI_NetworkBuilder::ComputeConnection( CAI_Node *pSrcNode, CAI_Node *pDestNode, Hull_t hull )
  2641. {
  2642. int srcId = pSrcNode->m_iID;
  2643. int destId = pDestNode->m_iID;
  2644. int result = 0;
  2645. trace_t tr;
  2646. // Set the size of the test hull
  2647. if ( m_pTestHull->GetHullType() != hull )
  2648. {
  2649. m_pTestHull->SetHullType( hull );
  2650. m_pTestHull->SetHullSizeNormal( true );
  2651. }
  2652. if ( !( m_pTestHull->GetFlags() & FL_ONGROUND ) )
  2653. {
  2654. DevWarning( 2, "OFFGROUND!\n" );
  2655. }
  2656. m_pTestHull->AddFlag( FL_ONGROUND );
  2657. // ==============================================================
  2658. // FIRST CHECK IF HULL CAN EVEN FIT AT THESE NODES
  2659. // ==============================================================
  2660. // @Note (toml 02-10-03): this should be optimized, caching the results of CanFitAtNode()
  2661. if ( !( pSrcNode->m_eNodeInfo & ( HullToBit( hull ) << NODE_ENT_FLAGS_SHIFT ) ) &&
  2662. !m_pTestHull->GetNavigator()->CanFitAtNode(srcId,NAI_Hull::TraceMask( hull )) )
  2663. {
  2664. DebugConnectMsg( srcId, destId, " Cannot fit at node %d\n", srcId );
  2665. return 0;
  2666. }
  2667. if ( !( pDestNode->m_eNodeInfo & ( HullToBit( hull ) << NODE_ENT_FLAGS_SHIFT ) ) &&
  2668. !m_pTestHull->GetNavigator()->CanFitAtNode(destId,NAI_Hull::TraceMask( hull )) )
  2669. {
  2670. DebugConnectMsg( srcId, destId, " Cannot fit at node %d\n", destId );
  2671. return 0;
  2672. }
  2673. // ==============================================================
  2674. // AIR NODES (FLYING)
  2675. // ==============================================================
  2676. if (pSrcNode->m_eNodeType == NODE_AIR || pDestNode->GetType() == NODE_AIR)
  2677. {
  2678. AI_PROFILE_SCOPE( CAI_Node_InitLinks_Air );
  2679. // Air nodes only connect to other air nodes and nothing else
  2680. if (pSrcNode->m_eNodeType == NODE_AIR && pDestNode->GetType() == NODE_AIR)
  2681. {
  2682. AI_TraceHull( pSrcNode->GetOrigin(), pDestNode->GetOrigin(), NAI_Hull::Mins(hull),NAI_Hull::Maxs(hull), NAI_Hull::TraceMask(hull), m_pTestHull, COLLISION_GROUP_NONE, &tr );
  2683. if (!tr.startsolid && tr.fraction == 1.0)
  2684. {
  2685. result |= bits_CAP_MOVE_FLY;
  2686. DebugConnectMsg( srcId, destId, " Connect by flying\n" );
  2687. }
  2688. }
  2689. }
  2690. // =============================================================================
  2691. // > CLIMBING
  2692. // =============================================================================
  2693. // If both are climb nodes just make sure they are above each other
  2694. // and there is room for the hull to pass between them
  2695. else if ((pSrcNode->m_eNodeType == NODE_CLIMB) && (pDestNode->GetType() == NODE_CLIMB))
  2696. {
  2697. AI_PROFILE_SCOPE( CAI_Node_InitLinks_Climb );
  2698. Vector srcPos = pSrcNode->GetPosition(hull);
  2699. Vector destPos = pDestNode->GetPosition(hull);
  2700. // If a code genereted climb dismount node the two origins will be the same
  2701. if (pSrcNode->GetOrigin() == pDestNode->GetOrigin())
  2702. {
  2703. AI_TraceHull( srcPos, destPos,
  2704. NAI_Hull::Mins(hull),NAI_Hull::Maxs(hull),
  2705. NAI_Hull::TraceMask(hull), m_pTestHull, COLLISION_GROUP_NONE, &tr );
  2706. if (!tr.startsolid && tr.fraction == 1.0)
  2707. {
  2708. result |= bits_CAP_MOVE_CLIMB;
  2709. DebugConnectMsg( srcId, destId, " Connect by climbing\n" );
  2710. }
  2711. }
  2712. else
  2713. {
  2714. if ( !IsInLineForClimb(srcPos, UTIL_YawToVector( pSrcNode->m_flYaw ), destPos, UTIL_YawToVector( pDestNode->m_flYaw ) ) )
  2715. {
  2716. Assert( !IsInLineForClimb(destPos, UTIL_YawToVector( pDestNode->m_flYaw ), srcPos, UTIL_YawToVector( pSrcNode->m_flYaw ) ) );
  2717. DebugConnectMsg( srcId, destId, " Not lined up for proper climbing\n" );
  2718. return 0;
  2719. }
  2720. AI_TraceHull( srcPos, destPos, NAI_Hull::Mins(hull),NAI_Hull::Maxs(hull), NAI_Hull::TraceMask(hull), m_pTestHull, COLLISION_GROUP_NONE, &tr );
  2721. if (!tr.startsolid && tr.fraction == 1.0)
  2722. {
  2723. result |= bits_CAP_MOVE_CLIMB;
  2724. DebugConnectMsg( srcId, destId, " Connect by climbing\n" );
  2725. }
  2726. }
  2727. }
  2728. // ====================================================
  2729. // > TWO LAND NODES
  2730. // =====================================================
  2731. else if ((pSrcNode->m_eNodeType == NODE_GROUND) || (pDestNode->GetType() == NODE_GROUND))
  2732. {
  2733. // BUG: this could use GroundMoveLimit, except there's no version of world but not brushes (doors open, etc).
  2734. // ====================================================
  2735. // > WALKING : walk the space between the nodes
  2736. // =====================================================
  2737. // in this loop we take tiny steps from the current node to the nodes that it links to, one at a time.
  2738. bool fStandFailed = false;
  2739. bool fWalkFailed = true;
  2740. bool fCrawlFailed = true;
  2741. AI_PROFILE_SCOPE_BEGIN( CAI_Node_InitLinks_Ground );
  2742. Vector srcPos = pSrcNode->GetPosition(hull);
  2743. Vector destPos = pDestNode->GetPosition(hull);
  2744. if (!m_pTestHull->GetMoveProbe()->CheckStandPosition( srcPos, NAI_Hull::TraceMask(hull)))
  2745. {
  2746. DebugConnectMsg( srcId, destId, " Failed to stand at %d\n", srcId );
  2747. fStandFailed = true;
  2748. }
  2749. if (!m_pTestHull->GetMoveProbe()->CheckStandPosition( destPos, NAI_Hull::TraceMask(hull)))
  2750. {
  2751. DebugConnectMsg( srcId, destId, " Failed to stand at %d\n", destId );
  2752. fStandFailed = true;
  2753. }
  2754. //if (hull == 0)
  2755. // DevMsg("from %.1f %.1f %.1f to %.1f %.1f %.1f\n", srcPos.x, srcPos.y, srcPos.z, destPos.x, destPos.y, destPos.z );
  2756. if ( !fStandFailed )
  2757. {
  2758. fWalkFailed = !m_pTestHull->GetMoveProbe()->TestGroundMove( srcPos, destPos, NAI_Hull::TraceMask(hull), AITGM_IGNORE_INITIAL_STAND_POS, NULL );
  2759. if ( fWalkFailed )
  2760. DebugConnectMsg( srcId, destId, " Failed to walk between nodes\n" );
  2761. }
  2762. // Add to our list of acceptable hulls
  2763. if ( !fStandFailed )
  2764. {
  2765. if ( !fWalkFailed )
  2766. {
  2767. result |= bits_CAP_MOVE_GROUND;
  2768. DebugConnectMsg( srcId, destId, " Nodes connect for ground movement\n" );
  2769. }
  2770. else
  2771. {
  2772. // Try it with a very large step height
  2773. fCrawlFailed = !m_pTestHull->GetMoveProbe()->TestGroundMove( srcPos, destPos, NAI_Hull::TraceMask(hull), AITGM_IGNORE_INITIAL_STAND_POS | AITGM_CRAWL_LARGE_STEPS, NULL );
  2774. if ( !fCrawlFailed )
  2775. {
  2776. DebugConnectMsg( srcId, destId, " Nodes connect for crawl movement\n" );
  2777. result |= bits_CAP_MOVE_CRAWL;
  2778. }
  2779. }
  2780. }
  2781. AI_PROFILE_SCOPE_END();
  2782. // =============================================================================
  2783. // > JUMPING : jump the space between the nodes, but only if walk failed
  2784. // =============================================================================
  2785. if (!fStandFailed && fWalkFailed && (pSrcNode->m_eNodeType == NODE_GROUND) && (pDestNode->GetType() == NODE_GROUND))
  2786. {
  2787. AI_PROFILE_SCOPE( CAI_Node_InitLinks_Jump );
  2788. Vector srcPos = pSrcNode->GetPosition(hull);
  2789. Vector destPos = pDestNode->GetPosition(hull);
  2790. // Jumps aren't bi-directional. We can jump down further than we can jump up so
  2791. // we have to test for either one
  2792. bool canDestJump = m_pTestHull->IsJumpLegal(srcPos, destPos, destPos);
  2793. bool canSrcJump = m_pTestHull->IsJumpLegal(destPos, srcPos, srcPos);
  2794. if (canDestJump || canSrcJump)
  2795. {
  2796. CAI_MoveProbe *pMoveProbe = m_pTestHull->GetMoveProbe();
  2797. bool fJumpLegal = false;
  2798. m_pTestHull->SetGravity(1.0);
  2799. AIMoveTrace_t moveTrace;
  2800. pMoveProbe->MoveLimit( NAV_JUMP, srcPos,destPos, NAI_Hull::TraceMask(hull), NULL, &moveTrace);
  2801. if (!IsMoveBlocked(moveTrace))
  2802. {
  2803. fJumpLegal = true;
  2804. }
  2805. pMoveProbe->MoveLimit( NAV_JUMP, destPos,srcPos, NAI_Hull::TraceMask(hull), NULL, &moveTrace);
  2806. if (!IsMoveBlocked(moveTrace))
  2807. {
  2808. fJumpLegal = true;
  2809. }
  2810. // Add to our list of accepable hulls
  2811. if (fJumpLegal)
  2812. {
  2813. result |= bits_CAP_MOVE_JUMP;
  2814. DebugConnectMsg( srcId, destId, " Nodes connect for jumping\n" );
  2815. }
  2816. }
  2817. }
  2818. }
  2819. return result;
  2820. }
  2821. //-------------------------------------
  2822. void CAI_NetworkBuilder::InitLinks(CAI_Network *pNetwork, CAI_Node *pNode)
  2823. {
  2824. AI_PROFILE_SCOPE( CAI_Node_InitLinks );
  2825. // -----------------------------------------------------
  2826. // Get test hull
  2827. // -----------------------------------------------------
  2828. m_pTestHull->GetNavigator()->SetNetwork( pNetwork );
  2829. // -----------------------------------------------------
  2830. // Initialize links to every node
  2831. // -----------------------------------------------------
  2832. for (int i = 0; i < pNetwork->NumNodes(); i++ )
  2833. {
  2834. // -------------------------------------------------
  2835. // Check for redundant link building
  2836. // -------------------------------------------------
  2837. DebugConnectMsg( pNode->m_iID, i, "Testing connection between %d and %d:\n", pNode->m_iID, i );
  2838. if (pNode->HasLink(i))
  2839. {
  2840. // A link has been already created when the other node was processed...
  2841. DebugConnectMsg( pNode->m_iID, i, " Nodes already connected\n" );
  2842. continue;
  2843. }
  2844. // ---------------------------------------------------------------------
  2845. // If link has been already created in other node just share it
  2846. // ---------------------------------------------------------------------
  2847. CAI_Node *pDestNode = pNetwork->GetNode( i );
  2848. CAI_Link *pOldLink = pDestNode->HasLink(pNode->m_iID);
  2849. if (pOldLink)
  2850. {
  2851. DebugConnectMsg( pNode->m_iID, i, " Sharing previously establish connection\n" );
  2852. ((CAI_Node *)pNode)->AddLink(pOldLink);
  2853. continue;
  2854. }
  2855. // Only check if the node is a neighbor
  2856. if ( m_NeighborsTable[pNode->m_iID].IsBitSet(pDestNode->m_iID) )
  2857. {
  2858. int acceptedMotions[NUM_HULLS];
  2859. bool bAllFailed = true;
  2860. if ( DebuggingConnect( pNode->m_iID, i ) )
  2861. {
  2862. DevMsg( "" ); // break here..
  2863. }
  2864. if ( !(pNode->m_eNodeInfo & bits_NODE_FALLEN) && !(pDestNode->m_eNodeInfo & bits_NODE_FALLEN) )
  2865. {
  2866. for (int hull = 0 ; hull < NUM_HULLS; hull++ )
  2867. {
  2868. DebugConnectMsg( pNode->m_iID, i, " Testing for hull %s\n", NAI_Hull::Name( (Hull_t)hull ) );
  2869. acceptedMotions[hull] = ComputeConnection( pNode, pDestNode, (Hull_t)hull );
  2870. if ( acceptedMotions[hull] != 0 )
  2871. bAllFailed = false;
  2872. }
  2873. }
  2874. else
  2875. DebugConnectMsg( pNode->m_iID, i, " No connection: one or both are fallen nodes\n" );
  2876. // If there were any passible hulls create link
  2877. if (!bAllFailed)
  2878. {
  2879. CAI_Link *pLink = pNetwork->CreateLink( pNode->m_iID, pDestNode->m_iID);
  2880. if ( pLink )
  2881. {
  2882. for (int hull=0;hull<NUM_HULLS;hull++)
  2883. {
  2884. pLink->m_iAcceptedMoveTypes[hull] = acceptedMotions[hull];
  2885. }
  2886. DebugConnectMsg( pNode->m_iID, i, " Added link\n" );
  2887. }
  2888. }
  2889. else
  2890. {
  2891. m_NeighborsTable[pNode->m_iID].Clear(pDestNode->m_iID);
  2892. DebugConnectMsg(pNode->m_iID, i, " NO LINK\n" );
  2893. }
  2894. }
  2895. else
  2896. DebugConnectMsg( pNode->m_iID, i, " NO LINK (not neighbors)\n" );
  2897. }
  2898. }
  2899. //-----------------------------------------------------------------------------