Team Fortress 2 Source Code as on 22/4/2020
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.

3248 lines
99 KiB

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