Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

749 lines
21 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Defines a connection (output-to-input) between two entities.
  4. //
  5. // The behavior in-game is as follows:
  6. //
  7. // When the given output in the source entity is triggered, the given
  8. // input in the target entity is called after a specified delay, and
  9. // the parameter override (if any) is passed to the input handler. If
  10. // there is no parameter override, the default parameter is passed.
  11. //
  12. // This behavior will occur a specified number of times before the
  13. // connection between the two entities is removed.
  14. //
  15. //=============================================================================//
  16. #include "stdafx.h"
  17. #include "EntityConnection.h"
  18. #include "MapEntity.h"
  19. #include "hammer.h"
  20. #include "MapDoc.h"
  21. #include "MapWorld.h"
  22. // memdbgon must be the last include file in a .cpp file!!!
  23. #include <tier0/memdbgon.h>
  24. //-----------------------------------------------------------------------------
  25. // Purpose: Constructor.
  26. //-----------------------------------------------------------------------------
  27. CEntityConnection::CEntityConnection(void)
  28. {
  29. memset(m_szSourceEntity, 0, sizeof(m_szSourceEntity));
  30. memset(m_szTargetEntity, 0, sizeof(m_szTargetEntity));
  31. memset(m_szOutput, 0, sizeof(m_szOutput));
  32. memset(m_szInput, 0, sizeof(m_szInput));
  33. memset(m_szParam, 0, sizeof(m_szParam));
  34. m_pSourceEntityList = new CMapEntityList;
  35. m_pTargetEntityList = new CMapEntityList;
  36. m_fDelay = 0;
  37. m_nTimesToFire = EVENT_FIRE_ALWAYS;
  38. }
  39. //-----------------------------------------------------------------------------
  40. // Purpose: Copy Constructor.
  41. //-----------------------------------------------------------------------------
  42. CEntityConnection::CEntityConnection( const CEntityConnection &Other )
  43. {
  44. m_pSourceEntityList = new CMapEntityList;
  45. m_pTargetEntityList = new CMapEntityList;
  46. *this = Other; // Invoke the Operator= to complete the construction job
  47. }
  48. //-----------------------------------------------------------------------------
  49. // Purpose: Destructor.
  50. //-----------------------------------------------------------------------------
  51. CEntityConnection::~CEntityConnection()
  52. {
  53. if ( m_pSourceEntityList )
  54. {
  55. m_pSourceEntityList->RemoveAll();
  56. delete m_pSourceEntityList;
  57. m_pSourceEntityList = NULL;
  58. }
  59. if ( m_pTargetEntityList )
  60. {
  61. m_pTargetEntityList->RemoveAll();
  62. delete m_pTargetEntityList;
  63. m_pTargetEntityList = NULL;
  64. }
  65. }
  66. //-----------------------------------------------------------------------------
  67. // Purpose: Operator= overload. Makes 'this' identical to 'Other'.
  68. //-----------------------------------------------------------------------------
  69. CEntityConnection &CEntityConnection::operator =(const CEntityConnection &Other)
  70. {
  71. strcpy(m_szSourceEntity, Other.m_szSourceEntity);
  72. strcpy(m_szTargetEntity, Other.m_szTargetEntity);
  73. strcpy(m_szOutput, Other.m_szOutput);
  74. strcpy(m_szInput, Other.m_szInput);
  75. strcpy(m_szParam, Other.m_szParam);
  76. m_fDelay = Other.m_fDelay;
  77. m_nTimesToFire = Other.m_nTimesToFire;
  78. // Invoke EntityList operator= to make copies.
  79. *m_pSourceEntityList = *Other.m_pSourceEntityList;
  80. *m_pTargetEntityList = *Other.m_pTargetEntityList;
  81. return(*this);
  82. }
  83. //-----------------------------------------------------------------------------
  84. // Purpose: Sets a new Input Name and sets links to any matching entities
  85. //-----------------------------------------------------------------------------
  86. void CEntityConnection::SetSourceName(const char *pszName)
  87. {
  88. // Save the name of the entity(ies)
  89. lstrcpyn(m_szSourceEntity, pszName ? pszName : "<<null>>", sizeof(m_szSourceEntity));
  90. // Update the source entity list
  91. // LinkSourceEntities(); // Changing the entity connection source name shouldnt change the source entity linkage, right?
  92. }
  93. //-----------------------------------------------------------------------------
  94. // Purpose: Sets a new Output Name and sets links to any matching entities
  95. //-----------------------------------------------------------------------------
  96. void CEntityConnection::SetTargetName(const char *pszName)
  97. {
  98. // Save the name of the entity(ies)
  99. lstrcpyn(m_szTargetEntity, pszName ? pszName : "<<null>>", sizeof(m_szTargetEntity));
  100. // Update the target entity list
  101. LinkTargetEntities();
  102. }
  103. //-----------------------------------------------------------------------------
  104. // Purpose: Links to any matching Source entities
  105. //-----------------------------------------------------------------------------
  106. void CEntityConnection::LinkSourceEntities()
  107. {
  108. // Empty out the existing entity list
  109. m_pSourceEntityList->RemoveAll();
  110. // Get a list of all the entities in the world
  111. CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
  112. if (pDoc)
  113. {
  114. CMapWorld *pWorld = pDoc->GetMapWorld();
  115. if (pWorld)
  116. {
  117. CMapEntityList matches;
  118. pWorld->FindEntitiesByName( matches, m_szSourceEntity, false );
  119. for ( int i = 0; i < matches.Count(); i++ )
  120. {
  121. CMapEntity *pEntity = matches.Element( i );
  122. m_pSourceEntityList->AddToTail( pEntity );
  123. //pEntity->Connection_Add( this ); // This should already be true on creation, investigate need for this func
  124. }
  125. }
  126. }
  127. }
  128. //-----------------------------------------------------------------------------
  129. // Purpose: Links to any matching Target entities
  130. //-----------------------------------------------------------------------------
  131. void CEntityConnection::LinkTargetEntities()
  132. {
  133. // Unlink us from the downstream entities.
  134. FOR_EACH_OBJ( *m_pTargetEntityList, pos )
  135. {
  136. CMapEntity *pEntity = m_pTargetEntityList->Element( pos );
  137. // If you hit this assert it means that an entity was deleted but not removed
  138. // from this entity's list of targets.
  139. ASSERT( pEntity != NULL );
  140. if ( pEntity )
  141. {
  142. pEntity->Upstream_Remove( this );
  143. }
  144. }
  145. // Empty out the existing entity list
  146. m_pTargetEntityList->RemoveAll();
  147. // Get a list of all the entities in the world
  148. CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
  149. if (pDoc)
  150. {
  151. CMapWorld *pWorld = pDoc->GetMapWorld();
  152. if (pWorld)
  153. {
  154. CMapEntityList matches;
  155. pWorld->FindEntitiesByName( matches, m_szTargetEntity, false );
  156. for ( int i = 0; i < matches.Count(); i++ )
  157. {
  158. CMapEntity *pEntity = matches.Element( i );
  159. m_pTargetEntityList->AddToTail( pEntity );
  160. // Special -- Add this connection to the target entity connection list
  161. pEntity->Upstream_Add( this );
  162. }
  163. }
  164. }
  165. }
  166. //------------------------------------------------------------------------------
  167. // Purpose: Tells if any of the target entities are visible.
  168. //------------------------------------------------------------------------------
  169. bool CEntityConnection::AreAnyTargetEntitiesVisible()
  170. {
  171. CMapEntityList *pList = GetTargetEntityList();
  172. for ( int iTarget=0; iTarget < pList->Count(); iTarget++ )
  173. {
  174. CMapEntity *pEntity = pList->Element( iTarget );
  175. if ( !pEntity )
  176. continue;
  177. if ( pEntity->IsVisible() )
  178. return true;
  179. }
  180. return false;
  181. }
  182. //------------------------------------------------------------------------------
  183. // Purpose: Returns true if output string is valid for all this entity
  184. //------------------------------------------------------------------------------
  185. bool CEntityConnection::ValidateOutput(const CMapEntity *pEntity, const char* pszOutput)
  186. {
  187. if (!pEntity)
  188. {
  189. return false;
  190. }
  191. GDclass* pClass = pEntity->GetClass();
  192. if (pClass != NULL)
  193. {
  194. if (pClass->FindOutput(pszOutput) == NULL)
  195. {
  196. return false;
  197. }
  198. }
  199. return true;
  200. }
  201. //------------------------------------------------------------------------------
  202. // Purpose : Returns true if output string is valid for all the entities in
  203. // the entity list
  204. // Input :
  205. // Output :
  206. //------------------------------------------------------------------------------
  207. bool CEntityConnection::ValidateOutput(const CMapEntityList *pEntityList, const char* pszOutput)
  208. {
  209. if (!pEntityList)
  210. {
  211. return false;
  212. }
  213. FOR_EACH_OBJ( *pEntityList, pos )
  214. {
  215. const CMapEntity* pEntity = pEntityList->Element(pos).GetObject();
  216. if ( !pEntity || !ValidateOutput(pEntity,pszOutput) )
  217. {
  218. return false;
  219. }
  220. }
  221. return true;
  222. }
  223. //------------------------------------------------------------------------------
  224. // Purpose: Returns true if the given entity list contains an entity of the
  225. // given target name
  226. //------------------------------------------------------------------------------
  227. bool CEntityConnection::ValidateTarget( const CMapEntityList *pEntityList, bool bVisibilityCheck, const char *pszTarget)
  228. {
  229. if (!pEntityList || !pszTarget)
  230. return false;
  231. // These procedural names are always assumed to exist.
  232. if (!stricmp(pszTarget, "!activator") || !stricmp(pszTarget, "!caller") || !stricmp(pszTarget, "!player") || !stricmp(pszTarget, "!self"))
  233. return true;
  234. FOR_EACH_OBJ( *pEntityList, pos )
  235. {
  236. const CMapEntity *pEntity = pEntityList->Element(pos).GetObject();
  237. if ( !pEntity || ( bVisibilityCheck && !pEntity->IsVisible() ) )
  238. continue;
  239. if (pEntity->NameMatches(pszTarget))
  240. return true;
  241. }
  242. return false;
  243. }
  244. //------------------------------------------------------------------------------
  245. // Purpose: Returns true if all entities with the given target name
  246. // have an input of the given input name
  247. //------------------------------------------------------------------------------
  248. bool CEntityConnection::ValidateInput( const char *pszTarget, const char *pszInput, bool bVisiblesOnly, CMapDoc *pDoc )
  249. {
  250. // Allow any input into !activator and !player.
  251. // dvs: TODO: pass in the entity to resolve !self and check input list
  252. if (!stricmp(pszTarget, "!activator") || !stricmp(pszTarget, "!caller") || !stricmp(pszTarget, "!player") || !stricmp(pszTarget, "!self"))
  253. {
  254. return true;
  255. }
  256. if ( pDoc == NULL )
  257. {
  258. pDoc = CMapDoc::GetActiveMapDoc();
  259. }
  260. CMapEntityList EntityList;
  261. pDoc->FindEntitiesByName(EntityList, pszTarget, bVisiblesOnly);
  262. if (EntityList.Count() == 0)
  263. {
  264. return false;
  265. }
  266. if (!MapEntityList_HasInput( &EntityList, pszInput))
  267. {
  268. return false;
  269. }
  270. return true;
  271. }
  272. //-----------------------------------------------------------------------------
  273. // Purpose: Finds any output connections from this entity that are bad.
  274. // "Bad" is defined as:
  275. //
  276. // 1) An output that this entity doesn't actually have.
  277. // 2) Connecting to a nonexistent entity.
  278. // 3) Connecting to a nonexistent input in an entity that exists.
  279. //
  280. // Input : pEntity - The entity to check for bad connections.
  281. //-----------------------------------------------------------------------------
  282. void CEntityConnection::FindBadConnections( CMapEntity *pEntity, bool bVisibilityCheck, CUtlVector<CEntityConnection *> &BadConnectionList, bool bIgnoreHiddenTargets, bool CheckAllDocuments )
  283. {
  284. BadConnectionList.RemoveAll();
  285. if ((!pEntity) || (pEntity->Connections_GetCount() == 0))
  286. {
  287. return;
  288. }
  289. // Get a list of all the entities in the world
  290. const CMapEntityList *pAllWorldEntities = NULL;
  291. CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
  292. if (pDoc)
  293. {
  294. CMapWorld *pWorld = pDoc->GetMapWorld();
  295. if (pWorld)
  296. {
  297. pAllWorldEntities = pWorld->EntityList_GetList();
  298. }
  299. }
  300. // For each connection
  301. int nConnCount = pEntity->Connections_GetCount();
  302. for (int i = 0; i < nConnCount; i++)
  303. {
  304. CEntityConnection *pConnection = pEntity->Connections_Get(i);
  305. if (pConnection != NULL)
  306. {
  307. if ( bIgnoreHiddenTargets )
  308. {
  309. if ( pConnection->GetTargetEntityList()->Count() > 0 && !pConnection->AreAnyTargetEntitiesVisible() )
  310. continue;
  311. }
  312. // Check validity of output for this entity
  313. if (!CEntityConnection::ValidateOutput(pEntity, pConnection->GetOutputName()))
  314. {
  315. BadConnectionList.AddToTail(pConnection);
  316. }
  317. else
  318. {
  319. bool bBadConnection = true;
  320. if ( CheckAllDocuments == false )
  321. {
  322. // Check validity of target entity (is it in the map?)
  323. if ( CEntityConnection::ValidateTarget(pAllWorldEntities, bVisibilityCheck, pConnection->GetTargetName() ) == true )
  324. {
  325. if ( CEntityConnection::ValidateInput(pConnection->GetTargetName(), pConnection->GetInputName(), true ) == true )
  326. {
  327. bBadConnection = false;
  328. }
  329. }
  330. }
  331. else
  332. {
  333. POSITION pos = APP()->pMapDocTemplate->GetFirstDocPosition();
  334. while( pos != NULL )
  335. {
  336. CDocument *pDoc = APP()->pMapDocTemplate->GetNextDoc( pos );
  337. CMapDoc *pMapDoc = dynamic_cast< CMapDoc * >( pDoc );
  338. if ( pMapDoc )
  339. {
  340. // Check validity of target entity (is it in the map?)
  341. if ( CEntityConnection::ValidateTarget( pMapDoc->GetMapWorld()->EntityList_GetList(), bVisibilityCheck, pConnection->GetTargetName() ) == true )
  342. {
  343. // Check validity of input
  344. if ( CEntityConnection::ValidateInput( pConnection->GetTargetName(), pConnection->GetInputName(), true, pMapDoc ) == true )
  345. {
  346. bBadConnection = false;
  347. break;
  348. }
  349. }
  350. }
  351. }
  352. }
  353. if ( bBadConnection == true )
  354. {
  355. BadConnectionList.AddToTail(pConnection);
  356. }
  357. }
  358. }
  359. }
  360. }
  361. //------------------------------------------------------------------------------
  362. // Purpose: Check if all the output connections in the given entity are valid.
  363. // Output : OUTPUTS_NONE if entity has no outputs
  364. // OUTPUTS_GOOD if all entity outputs are good
  365. // OUTPUTS_BAD if any entity output is bad
  366. //------------------------------------------------------------------------------
  367. int CEntityConnection::ValidateOutputConnections( CMapEntity *pEntity, bool bVisibilityCheck, bool bIgnoreHiddenTargets, bool CheckAllDocuments )
  368. {
  369. if (!pEntity)
  370. {
  371. return CONNECTION_NONE;
  372. }
  373. if (pEntity->Connections_GetCount() == 0)
  374. {
  375. return CONNECTION_NONE;
  376. }
  377. CUtlVector<CEntityConnection *> BadConnectionList;
  378. FindBadConnections( pEntity, bVisibilityCheck, BadConnectionList, bIgnoreHiddenTargets, CheckAllDocuments );
  379. if (BadConnectionList.Count() > 0)
  380. {
  381. return CONNECTION_BAD;
  382. }
  383. return CONNECTION_GOOD;
  384. }
  385. //-----------------------------------------------------------------------------
  386. // Purpose: Fixes any output connections from this entity that are bad.
  387. // Input : pEntity - The entity to fix.
  388. //-----------------------------------------------------------------------------
  389. void CEntityConnection::FixBadConnections(CMapEntity *pEntity, bool bVisibilityCheck )
  390. {
  391. CUtlVector<CEntityConnection *> BadConnectionList;
  392. FindBadConnections(pEntity, bVisibilityCheck, BadConnectionList);
  393. // Remove the bad connections.
  394. int nBadConnCount = BadConnectionList.Count();
  395. for (int i = 0; i < nBadConnCount; i++)
  396. {
  397. CEntityConnection *pConnection = BadConnectionList.Element(i);
  398. pEntity->Connections_Remove( pConnection );
  399. //
  400. // Remove the connection from the upstream list of all entities it targets.
  401. //
  402. CMapEntityList *pTargetList = pConnection->GetTargetEntityList();
  403. if ( pTargetList )
  404. {
  405. FOR_EACH_OBJ( *pTargetList, pos )
  406. {
  407. CMapEntity *pEntity = pTargetList->Element( pos );
  408. // If you hit this assert it means that an entity was deleted but not removed
  409. // from this entity's list of targets.
  410. ASSERT( pEntity != NULL );
  411. if ( pEntity )
  412. {
  413. pEntity->Upstream_Remove( pConnection );
  414. }
  415. }
  416. }
  417. delete pConnection;
  418. }
  419. }
  420. //------------------------------------------------------------------------------
  421. // Purpose: Check if all the output connections in the given entity are valid.
  422. // Output : INPUTS_NONE, // if entity list has no inputs
  423. // INPUTS_GOOD, // if all entity inputs are good
  424. // INPUTS_BAD, // if any entity input is bad
  425. //------------------------------------------------------------------------------
  426. int CEntityConnection::ValidateInputConnections(CMapEntity *pEntity, bool bVisibilityCheck)
  427. {
  428. if (!pEntity)
  429. {
  430. return CONNECTION_NONE;
  431. }
  432. // No inputs if entity doesn't have a target name
  433. const char *pszTargetName = pEntity->GetKeyValue("targetname");
  434. if (!pszTargetName)
  435. {
  436. return CONNECTION_NONE;
  437. }
  438. GDclass *pClass = pEntity->GetClass();
  439. if (!pClass)
  440. {
  441. return CONNECTION_NONE;
  442. }
  443. // Get a list of all the entities in the world
  444. const CMapEntityList *pAllWorldEntities = NULL;
  445. CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
  446. if (pDoc)
  447. {
  448. CMapWorld *pWorld = pDoc->GetMapWorld();
  449. if (pWorld)
  450. {
  451. pAllWorldEntities = pWorld->EntityList_GetList();
  452. }
  453. }
  454. // Look at outputs from each entity in the world
  455. bool bHaveConnection = false;
  456. FOR_EACH_OBJ( *pAllWorldEntities, pos )
  457. {
  458. const CMapEntity *pTestEntity = pAllWorldEntities->Element(pos).GetObject();
  459. if (pTestEntity == NULL)
  460. continue;
  461. if ( bVisibilityCheck && !pTestEntity->IsVisible() )
  462. continue;
  463. int nConnCount = pTestEntity->Connections_GetCount();
  464. for (int i = 0; i < nConnCount; i++)
  465. {
  466. // If the connection targets me
  467. CEntityConnection *pConnection = pTestEntity->Connections_Get(i);
  468. if ( pConnection && pEntity->NameMatches( pConnection->GetTargetName() ) )
  469. {
  470. // Validate output
  471. if (!ValidateOutput(pTestEntity, pConnection->GetOutputName()))
  472. {
  473. return CONNECTION_BAD;
  474. }
  475. // Validate input
  476. if (pClass->FindInput(pConnection->GetInputName()) == NULL)
  477. {
  478. return CONNECTION_BAD;
  479. }
  480. // FIXME -- Validate the upstream connections the target entities.
  481. bHaveConnection = true;
  482. }
  483. }
  484. }
  485. if (bHaveConnection)
  486. {
  487. return CONNECTION_GOOD;
  488. }
  489. return CONNECTION_NONE;
  490. }
  491. //-----------------------------------------------------------------------------
  492. // Purpose: Compares by delays. Used as a secondary sort by all other columns.
  493. //-----------------------------------------------------------------------------
  494. int CALLBACK CEntityConnection::CompareDelaysSecondary(CEntityConnection *pConn1, CEntityConnection *pConn2, SortDirection_t eDirection)
  495. {
  496. float fDelay1;
  497. float fDelay2;
  498. if (eDirection == Sort_Ascending)
  499. {
  500. fDelay1 = pConn1->GetDelay();
  501. fDelay2 = pConn2->GetDelay();
  502. }
  503. else
  504. {
  505. fDelay1 = pConn2->GetDelay();
  506. fDelay2 = pConn1->GetDelay();
  507. }
  508. if (fDelay1 < fDelay2)
  509. {
  510. return(-1);
  511. }
  512. else if (fDelay1 > fDelay2)
  513. {
  514. return(1);
  515. }
  516. return(0);
  517. }
  518. //-----------------------------------------------------------------------------
  519. // Purpose: Compares by delays, does a secondary compare by output name.
  520. //-----------------------------------------------------------------------------
  521. int CALLBACK CEntityConnection::CompareDelays(CEntityConnection *pConn1, CEntityConnection *pConn2, SortDirection_t eDirection)
  522. {
  523. int nReturn = CompareDelaysSecondary(pConn1, pConn2, eDirection);
  524. if (nReturn != 0)
  525. {
  526. return(nReturn);
  527. }
  528. //
  529. // Always do a secondary sort by output name.
  530. //
  531. return(CompareOutputNames(pConn1, pConn2, Sort_Ascending));
  532. }
  533. //-----------------------------------------------------------------------------
  534. // Purpose: Compares by output name, does a secondary compare by delay.
  535. //-----------------------------------------------------------------------------
  536. int CALLBACK CEntityConnection::CompareOutputNames(CEntityConnection *pConn1, CEntityConnection *pConn2, SortDirection_t eDirection)
  537. {
  538. int nReturn = 0;
  539. if (eDirection == Sort_Ascending)
  540. {
  541. nReturn = stricmp(pConn1->GetOutputName(), pConn2->GetOutputName());
  542. }
  543. else
  544. {
  545. nReturn = stricmp(pConn2->GetOutputName(), pConn1->GetOutputName());
  546. }
  547. //
  548. // Always do a secondary sort by delay.
  549. //
  550. if (nReturn == 0)
  551. {
  552. nReturn = CompareDelaysSecondary(pConn1, pConn2, Sort_Ascending);
  553. }
  554. return(nReturn);
  555. }
  556. //-----------------------------------------------------------------------------
  557. // Purpose: Compares by input name, does a secondary compare by delay.
  558. //-----------------------------------------------------------------------------
  559. int CALLBACK CEntityConnection::CompareInputNames(CEntityConnection *pConn1, CEntityConnection *pConn2, SortDirection_t eDirection)
  560. {
  561. int nReturn = 0;
  562. if (eDirection == Sort_Ascending)
  563. {
  564. nReturn = stricmp(pConn1->GetInputName(), pConn2->GetInputName());
  565. }
  566. else
  567. {
  568. nReturn = stricmp(pConn2->GetInputName(), pConn1->GetInputName());
  569. }
  570. //
  571. // Always do a secondary sort by delay.
  572. //
  573. if (nReturn == 0)
  574. {
  575. nReturn = CompareDelaysSecondary(pConn1, pConn2, Sort_Ascending);
  576. }
  577. return(nReturn);
  578. }
  579. //-----------------------------------------------------------------------------
  580. // Purpose: Compares by source name, does a secondary compare by delay.
  581. //-----------------------------------------------------------------------------
  582. int CALLBACK CEntityConnection::CompareSourceNames(CEntityConnection *pConn1, CEntityConnection *pConn2, SortDirection_t eDirection)
  583. {
  584. int nReturn = 0;
  585. if (eDirection == Sort_Ascending)
  586. {
  587. nReturn = CompareEntityNames(pConn1->GetSourceName(), pConn2->GetSourceName());
  588. }
  589. else
  590. {
  591. nReturn = CompareEntityNames(pConn2->GetSourceName(), pConn1->GetSourceName());
  592. }
  593. //
  594. // Always do a secondary sort by delay.
  595. //
  596. if (nReturn == 0)
  597. {
  598. nReturn = CompareDelaysSecondary(pConn1, pConn2, Sort_Ascending);
  599. }
  600. return(nReturn);
  601. }
  602. //-----------------------------------------------------------------------------
  603. // Purpose: Compares by target name, does a secondary compare by delay.
  604. //-----------------------------------------------------------------------------
  605. int CALLBACK CEntityConnection::CompareTargetNames(CEntityConnection *pConn1, CEntityConnection *pConn2, SortDirection_t eDirection)
  606. {
  607. int nReturn = 0;
  608. if (eDirection == Sort_Ascending)
  609. {
  610. nReturn = CompareEntityNames(pConn1->GetTargetName(), pConn2->GetTargetName());
  611. }
  612. else
  613. {
  614. nReturn = CompareEntityNames(pConn2->GetTargetName(), pConn1->GetTargetName());
  615. }
  616. //
  617. // Always do a secondary sort by delay.
  618. //
  619. if (nReturn == 0)
  620. {
  621. nReturn = CompareDelaysSecondary(pConn1, pConn2, Sort_Ascending);
  622. }
  623. return(nReturn);
  624. }