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.

518 lines
16 KiB

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