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.

496 lines
15 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Entity which alters the relationships between entities via entity I/O
  4. //
  5. //=====================================================================================//
  6. #include "cbase.h"
  7. #include "ndebugoverlay.h"
  8. #include "ai_basenpc.h"
  9. // memdbgon must be the last include file in a .cpp file!!!
  10. #include "tier0/memdbgon.h"
  11. #define SF_RELATIONSHIP_NOTIFY_SUBJECT (1<<0) // Alert the subject of the change and give them a memory of the target entity
  12. #define SF_RELATIONSHIP_NOTIFY_TARGET (1<<1) // Alert the target of the change and give them a memory of the subject entity
  13. enum
  14. {
  15. NOT_REVERTING,
  16. REVERTING_TO_PREV,
  17. REVERTING_TO_DEFAULT,
  18. };
  19. //=========================================================
  20. //=========================================================
  21. class CAI_Relationship : public CBaseEntity, public IEntityListener
  22. {
  23. DECLARE_CLASS( CAI_Relationship, CBaseEntity );
  24. public:
  25. CAI_Relationship() : m_iPreviousDisposition( -1 ) { }
  26. void Spawn();
  27. void Activate();
  28. void SetActive( bool bActive );
  29. void ChangeRelationships( int disposition, int iReverting, CBaseEntity *pActivator = NULL, CBaseEntity *pCaller = NULL );
  30. void ApplyRelationship( CBaseEntity *pActivator = NULL, CBaseEntity *pCaller = NULL );
  31. void RevertRelationship( CBaseEntity *pActivator = NULL, CBaseEntity *pCaller = NULL );
  32. void RevertToDefaultRelationship( CBaseEntity *pActivator = NULL, CBaseEntity *pCaller = NULL );
  33. void UpdateOnRemove();
  34. void OnRestore();
  35. bool IsASubject( CBaseEntity *pEntity );
  36. bool IsATarget( CBaseEntity *pEntity );
  37. void OnEntitySpawned( CBaseEntity *pEntity );
  38. void OnEntityDeleted( CBaseEntity *pEntity );
  39. private:
  40. void ApplyRelationshipThink( void );
  41. CBaseEntity *FindEntityForProceduralName( string_t iszName, CBaseEntity *pActivator, CBaseEntity *pCaller );
  42. void DiscloseNPCLocation( CBaseCombatCharacter *pSubject, CBaseCombatCharacter *pTarget );
  43. string_t m_iszSubject;
  44. int m_iDisposition;
  45. int m_iRank;
  46. bool m_fStartActive;
  47. bool m_bIsActive;
  48. int m_iPreviousDisposition;
  49. float m_flRadius;
  50. int m_iPreviousRank;
  51. bool m_bReciprocal;
  52. public:
  53. // Input functions
  54. void InputApplyRelationship( inputdata_t &inputdata );
  55. void InputRevertRelationship( inputdata_t &inputdata );
  56. void InputRevertToDefaultRelationship( inputdata_t &inputdata );
  57. DECLARE_DATADESC();
  58. };
  59. LINK_ENTITY_TO_CLASS( ai_relationship, CAI_Relationship );
  60. BEGIN_DATADESC( CAI_Relationship )
  61. DEFINE_THINKFUNC( ApplyRelationshipThink ),
  62. DEFINE_KEYFIELD( m_iszSubject, FIELD_STRING, "subject" ),
  63. DEFINE_KEYFIELD( m_iDisposition, FIELD_INTEGER, "disposition" ),
  64. DEFINE_KEYFIELD( m_iRank, FIELD_INTEGER, "rank" ),
  65. DEFINE_KEYFIELD( m_fStartActive, FIELD_BOOLEAN, "StartActive" ),
  66. DEFINE_FIELD( m_bIsActive, FIELD_BOOLEAN ),
  67. DEFINE_KEYFIELD( m_flRadius, FIELD_FLOAT, "radius" ),
  68. DEFINE_FIELD( m_iPreviousDisposition, FIELD_INTEGER ),
  69. DEFINE_FIELD( m_iPreviousRank, FIELD_INTEGER ),
  70. DEFINE_KEYFIELD( m_bReciprocal, FIELD_BOOLEAN, "reciprocal" ),
  71. // Inputs
  72. DEFINE_INPUTFUNC( FIELD_VOID, "ApplyRelationship", InputApplyRelationship ),
  73. DEFINE_INPUTFUNC( FIELD_VOID, "RevertRelationship", InputRevertRelationship ),
  74. DEFINE_INPUTFUNC( FIELD_VOID, "RevertToDefaultRelationship", InputRevertToDefaultRelationship ),
  75. END_DATADESC()
  76. //-----------------------------------------------------------------------------
  77. // Purpose:
  78. //-----------------------------------------------------------------------------
  79. void CAI_Relationship::Spawn()
  80. {
  81. m_bIsActive = false;
  82. if (m_iszSubject == NULL_STRING)
  83. {
  84. DevWarning("ai_relationship '%s' with no subject specified, removing.\n", GetDebugName());
  85. UTIL_Remove(this);
  86. }
  87. else if (m_target == NULL_STRING)
  88. {
  89. DevWarning("ai_relationship '%s' with no target specified, removing.\n", GetDebugName());
  90. UTIL_Remove(this);
  91. }
  92. }
  93. //---------------------------------------------------------
  94. //---------------------------------------------------------
  95. void CAI_Relationship::Activate()
  96. {
  97. if ( m_fStartActive )
  98. {
  99. ApplyRelationship();
  100. // Clear this flag so that nothing happens when the level is loaded (which calls activate again)
  101. m_fStartActive = false;
  102. }
  103. BaseClass::Activate();
  104. }
  105. //-----------------------------------------------------------------------------
  106. // Purpose:
  107. // Input : bActive -
  108. //-----------------------------------------------------------------------------
  109. void CAI_Relationship::SetActive( bool bActive )
  110. {
  111. if ( bActive && !m_bIsActive )
  112. {
  113. // Start getting entity updates!
  114. gEntList.AddListenerEntity( this );
  115. }
  116. else if ( !bActive && m_bIsActive )
  117. {
  118. // Stop getting entity updates!
  119. gEntList.RemoveListenerEntity( this );
  120. }
  121. m_bIsActive = bActive;
  122. }
  123. //---------------------------------------------------------
  124. //---------------------------------------------------------
  125. void CAI_Relationship::InputApplyRelationship( inputdata_t &inputdata )
  126. {
  127. ApplyRelationship( inputdata.pActivator, inputdata.pCaller );
  128. }
  129. //---------------------------------------------------------
  130. //---------------------------------------------------------
  131. void CAI_Relationship::InputRevertRelationship( inputdata_t &inputdata )
  132. {
  133. RevertRelationship( inputdata.pActivator, inputdata.pCaller );
  134. }
  135. //---------------------------------------------------------
  136. //---------------------------------------------------------
  137. void CAI_Relationship::InputRevertToDefaultRelationship( inputdata_t &inputdata )
  138. {
  139. RevertToDefaultRelationship( inputdata.pActivator, inputdata.pCaller );
  140. }
  141. //-----------------------------------------------------------------------------
  142. // Purpose: This think function is used to wait until the player has properly
  143. // spawned, after all the NPCs have spawned. Once that occurs, this
  144. // function terminates.
  145. //-----------------------------------------------------------------------------
  146. void CAI_Relationship::ApplyRelationshipThink( void )
  147. {
  148. // Call down to the base until the player has properly spawned
  149. ApplyRelationship();
  150. }
  151. //---------------------------------------------------------
  152. // Purpose: Applies the desired relationships to an entity
  153. //---------------------------------------------------------
  154. void CAI_Relationship::ApplyRelationship( CBaseEntity *pActivator, CBaseEntity *pCaller )
  155. {
  156. // @TODO (toml 10-22-04): sort out MP relationships
  157. // The player spawns slightly after the NPCs, meaning that if we don't wait, the
  158. // player will miss any relationships placed on them.
  159. if ( AI_IsSinglePlayer() && !UTIL_GetLocalPlayer() )
  160. {
  161. SetThink( &CAI_Relationship::ApplyRelationshipThink );
  162. SetNextThink( gpGlobals->curtime );
  163. }
  164. if ( !m_bIsActive )
  165. {
  166. SetActive( true );
  167. }
  168. ChangeRelationships( m_iDisposition, NOT_REVERTING, pActivator, pCaller );
  169. }
  170. //---------------------------------------------------------
  171. //---------------------------------------------------------
  172. void CAI_Relationship::RevertRelationship( CBaseEntity *pActivator, CBaseEntity *pCaller )
  173. {
  174. if ( m_bIsActive )
  175. {
  176. ChangeRelationships( m_iPreviousDisposition, REVERTING_TO_PREV, pActivator, pCaller );
  177. SetActive( false );
  178. }
  179. }
  180. //---------------------------------------------------------
  181. //---------------------------------------------------------
  182. void CAI_Relationship::RevertToDefaultRelationship( CBaseEntity *pActivator, CBaseEntity *pCaller )
  183. {
  184. if ( m_bIsActive )
  185. {
  186. ChangeRelationships( -1, REVERTING_TO_DEFAULT, pActivator, pCaller );
  187. SetActive( false );
  188. }
  189. }
  190. //---------------------------------------------------------
  191. //---------------------------------------------------------
  192. void CAI_Relationship::UpdateOnRemove()
  193. {
  194. gEntList.RemoveListenerEntity( this );
  195. // @TODO (toml 07-21-04): Should this actually revert on kill?
  196. // RevertRelationship();
  197. BaseClass::UpdateOnRemove();
  198. }
  199. //---------------------------------------------------------
  200. //---------------------------------------------------------
  201. void CAI_Relationship::OnRestore()
  202. {
  203. BaseClass::OnRestore();
  204. if ( m_bIsActive )
  205. {
  206. gEntList.AddListenerEntity( this );
  207. }
  208. }
  209. //---------------------------------------------------------
  210. //---------------------------------------------------------
  211. bool CAI_Relationship::IsASubject( CBaseEntity *pEntity )
  212. {
  213. if( pEntity->NameMatches( m_iszSubject ) )
  214. return true;
  215. if( pEntity->ClassMatches( m_iszSubject ) )
  216. return true;
  217. return false;
  218. }
  219. //---------------------------------------------------------
  220. //---------------------------------------------------------
  221. bool CAI_Relationship::IsATarget( CBaseEntity *pEntity )
  222. {
  223. if( pEntity->NameMatches( m_target ) )
  224. return true;
  225. if( pEntity->ClassMatches( m_target ) )
  226. return true;
  227. return false;
  228. }
  229. //---------------------------------------------------------
  230. //---------------------------------------------------------
  231. void CAI_Relationship::OnEntitySpawned( CBaseEntity *pEntity )
  232. {
  233. // NOTE: This cannot use the procedural entity finding code since that only occurs on
  234. // inputs and not passively.
  235. if ( IsATarget( pEntity ) || IsASubject( pEntity ) )
  236. {
  237. ApplyRelationship();
  238. }
  239. }
  240. //---------------------------------------------------------
  241. //---------------------------------------------------------
  242. void CAI_Relationship::OnEntityDeleted( CBaseEntity *pEntity )
  243. {
  244. }
  245. //-----------------------------------------------------------------------------
  246. // Purpose: Translate special tokens for inputs
  247. // Input : iszName - Name to check
  248. // *pActivator - Activator
  249. // *pCaller - Caller
  250. // Output : CBaseEntity - Entity that matches (NULL if none)
  251. //-----------------------------------------------------------------------------
  252. #define ACTIVATOR_KEYNAME "!activator"
  253. #define CALLER_KEYNAME "!caller"
  254. CBaseEntity *CAI_Relationship::FindEntityForProceduralName( string_t iszName, CBaseEntity *pActivator, CBaseEntity *pCaller )
  255. {
  256. // Handle the activator token
  257. if ( iszName == AllocPooledString( ACTIVATOR_KEYNAME ) )
  258. return pActivator;
  259. // Handle the caller token
  260. if ( iszName == AllocPooledString( CALLER_KEYNAME ) )
  261. return pCaller;
  262. return NULL;
  263. }
  264. //-----------------------------------------------------------------------------
  265. // Purpose: Disclose the location of the target entity to the subject via a memory
  266. // Input : *pSubject - Entity to gain the memory of the target's location
  267. // *pTarget - Entity who's location will be disclosed
  268. //-----------------------------------------------------------------------------
  269. void CAI_Relationship::DiscloseNPCLocation( CBaseCombatCharacter *pSubject, CBaseCombatCharacter *pTarget )
  270. {
  271. if ( pSubject == NULL || pTarget == NULL )
  272. return;
  273. CAI_BaseNPC *pNPC = pSubject->MyNPCPointer();
  274. if ( pNPC != NULL )
  275. {
  276. pNPC->UpdateEnemyMemory( pTarget, pTarget->GetAbsOrigin() );
  277. }
  278. }
  279. //---------------------------------------------------------
  280. //---------------------------------------------------------
  281. void CAI_Relationship::ChangeRelationships( int disposition, int iReverting, CBaseEntity *pActivator, CBaseEntity *pCaller )
  282. {
  283. if( iReverting != NOT_REVERTING && m_iPreviousDisposition == -1 )
  284. {
  285. // Trying to revert without having ever set the relationships!
  286. DevMsg( 2, "ai_relationship cannot revert changes before they are applied!\n");
  287. return;
  288. }
  289. const int MAX_HANDLED = 512;
  290. CUtlVectorFixed<CBaseCombatCharacter *, MAX_HANDLED> subjectList;
  291. CUtlVectorFixed<CBaseCombatCharacter *, MAX_HANDLED> targetList;
  292. // Add any special subjects we found
  293. CBaseEntity *pSpecialSubject = FindEntityForProceduralName( m_iszSubject, pActivator, pCaller );
  294. if ( pSpecialSubject && pSpecialSubject->MyCombatCharacterPointer() )
  295. {
  296. subjectList.AddToTail( pSpecialSubject->MyCombatCharacterPointer() );
  297. }
  298. // Add any special targets we found
  299. CBaseEntity *pSpecialTarget = FindEntityForProceduralName( m_target, pActivator, pCaller );
  300. if ( pSpecialTarget && pSpecialTarget->MyCombatCharacterPointer() )
  301. {
  302. targetList.AddToTail( pSpecialTarget->MyCombatCharacterPointer() );
  303. }
  304. // -------------------------------
  305. // Search for targets and subjects
  306. // -------------------------------
  307. float radiusSq = Square( m_flRadius );
  308. // Search players first
  309. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  310. {
  311. if ( subjectList.Count() == MAX_HANDLED || targetList.Count() == MAX_HANDLED )
  312. {
  313. DevMsg( "Too many entities handled by ai_relationship %s\n", GetDebugName() );
  314. break;
  315. }
  316. CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
  317. if ( pPlayer )
  318. {
  319. if( IsASubject( pPlayer ) )
  320. {
  321. if ( m_flRadius == 0.0 || GetAbsOrigin().DistToSqr( pPlayer->GetAbsOrigin() ) <= radiusSq )
  322. subjectList.AddToTail( pPlayer );
  323. }
  324. else if( IsATarget( pPlayer ) )
  325. {
  326. targetList.AddToTail( pPlayer );
  327. }
  328. }
  329. }
  330. // Search NPCs
  331. for ( int i = 0; i < g_AI_Manager.NumAIs(); i++ )
  332. {
  333. if ( subjectList.Count() == MAX_HANDLED || targetList.Count() == MAX_HANDLED )
  334. {
  335. DevMsg( "Too many entities handled by ai_relationship %s\n", GetDebugName() );
  336. break;
  337. }
  338. CAI_BaseNPC *pNPC = (g_AI_Manager.AccessAIs())[i];
  339. if ( pNPC )
  340. {
  341. if( IsASubject( pNPC ) )
  342. {
  343. if ( m_flRadius == 0.0 || GetAbsOrigin().DistToSqr( pNPC->GetAbsOrigin() ) <= radiusSq )
  344. subjectList.AddToTail( pNPC );
  345. }
  346. else if( IsATarget( pNPC ) )
  347. {
  348. targetList.AddToTail( pNPC );
  349. }
  350. }
  351. }
  352. // If either list is still empty, we have a problem.
  353. if( subjectList.Count() == 0 )
  354. {
  355. DevMsg( 2, "ai_relationship '%s' finds no subject(s) called: %s\n", GetDebugName(), STRING( m_iszSubject ) );
  356. return;
  357. }
  358. else if ( targetList.Count() == 0 )
  359. {
  360. DevMsg( 2, "ai_relationship '%s' finds no target(s) called: %s\n", GetDebugName(), STRING( m_target ) );
  361. return;
  362. }
  363. // Ok, lists are populated. Apply all relationships.
  364. for ( int i = 0 ; i < subjectList.Count(); i++ )
  365. {
  366. CBaseCombatCharacter *pSubject = subjectList[ i ];
  367. for ( int j = 0 ; j < targetList.Count(); j++ )
  368. {
  369. CBaseCombatCharacter *pTarget = targetList[ j ];
  370. if ( m_iPreviousDisposition == -1 && iReverting == NOT_REVERTING )
  371. {
  372. // Set previous disposition.
  373. m_iPreviousDisposition = pSubject->IRelationType( pTarget );
  374. m_iPreviousRank = pSubject->IRelationPriority( pTarget );
  375. }
  376. if ( iReverting == REVERTING_TO_PREV )
  377. {
  378. pSubject->AddEntityRelationship( pTarget, (Disposition_t)m_iPreviousDisposition, m_iPreviousRank );
  379. if( m_bReciprocal )
  380. {
  381. pTarget->AddEntityRelationship( pSubject, (Disposition_t)m_iPreviousDisposition, m_iPreviousRank );
  382. }
  383. }
  384. else if ( iReverting == REVERTING_TO_DEFAULT )
  385. {
  386. pSubject->RemoveEntityRelationship( pTarget );
  387. if( m_bReciprocal )
  388. {
  389. pTarget->RemoveEntityRelationship( pSubject );
  390. }
  391. }
  392. else if( pSubject->IRelationType(pTarget) != disposition ||
  393. pSubject->IRelationPriority(pTarget) != m_iRank ||
  394. HasSpawnFlags( SF_RELATIONSHIP_NOTIFY_SUBJECT ) ||
  395. HasSpawnFlags( SF_RELATIONSHIP_NOTIFY_TARGET ) )
  396. {
  397. // Apply the relationship to the subject
  398. pSubject->AddEntityRelationship( pTarget, (Disposition_t)disposition, m_iRank );
  399. // Make the subject aware of the target
  400. if ( HasSpawnFlags( SF_RELATIONSHIP_NOTIFY_SUBJECT ) )
  401. {
  402. DiscloseNPCLocation( pSubject, pTarget );
  403. }
  404. // Make the target aware of the subject
  405. if ( HasSpawnFlags( SF_RELATIONSHIP_NOTIFY_TARGET ) )
  406. {
  407. DiscloseNPCLocation( pTarget, pSubject );
  408. }
  409. // This relationship is applied to target and subject alike
  410. if ( m_bReciprocal )
  411. {
  412. // Apply the relationship to the target
  413. pTarget->AddEntityRelationship( pSubject, (Disposition_t)disposition, m_iRank );
  414. }
  415. }
  416. }
  417. }
  418. }