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.

688 lines
20 KiB

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