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.

219 lines
7.8 KiB

  1. //====== Copyright � 1996-2005, Valve Corporation, All rights reserved. =======
  2. //
  3. // Purpose: AI system that makes NPCs verbally respond to game events
  4. //
  5. //=============================================================================
  6. #include "cbase.h"
  7. #include "ai_eventresponse.h"
  8. #include "ai_basenpc.h"
  9. // NOTE: This has to be the last file included!
  10. #include "tier0/memdbgon.h"
  11. ConVar ai_debug_eventresponses( "ai_debug_eventresponses", "0", FCVAR_NONE, "Set to 1 to see all NPC response events trigger, and which NPCs choose to respond to them." );
  12. //-----------------------------------------------------------------------------
  13. // Purpose:
  14. //-----------------------------------------------------------------------------
  15. CNPCEventResponseSystem g_NPCEventResponseSystem( "CNPCEventResponseSystem" );
  16. CNPCEventResponseSystem *NPCEventResponse()
  17. {
  18. return &g_NPCEventResponseSystem;
  19. }
  20. //-----------------------------------------------------------------------------
  21. // Purpose:
  22. //-----------------------------------------------------------------------------
  23. void CNPCEventResponseSystem::LevelInitPreEntity( void )
  24. {
  25. m_ActiveEvents.Purge();
  26. m_flNextEventPoll = 0;
  27. }
  28. //-----------------------------------------------------------------------------
  29. // Purpose:
  30. //-----------------------------------------------------------------------------
  31. void CNPCEventResponseSystem::TriggerEvent( const char *pResponse, bool bForce, bool bCancelScript )
  32. {
  33. m_flNextEventPoll = gpGlobals->curtime;
  34. // Find the event by name
  35. int iIndex = m_ActiveEvents.Find( pResponse );
  36. if ( iIndex == m_ActiveEvents.InvalidIndex() )
  37. {
  38. storedevent_t newEvent;
  39. newEvent.flEventTime = gpGlobals->curtime;
  40. newEvent.flNextResponseTime = 0;
  41. newEvent.bForce = bForce;
  42. newEvent.bCancelScript = bCancelScript;
  43. newEvent.bPreventExpiration = false;
  44. m_ActiveEvents.Insert( pResponse, newEvent );
  45. if ( ai_debug_eventresponses.GetBool() )
  46. {
  47. Msg( "NPCEVENTRESPONSE: (%.2f) Trigger fired for event named: %s\n", gpGlobals->curtime, pResponse );
  48. }
  49. }
  50. else
  51. {
  52. // Update the trigger time
  53. m_ActiveEvents[iIndex].flEventTime = gpGlobals->curtime;
  54. m_ActiveEvents[iIndex].bForce = bForce;
  55. m_ActiveEvents[iIndex].bCancelScript = bCancelScript;
  56. if ( ai_debug_eventresponses.GetBool() )
  57. {
  58. Msg( "NPCEVENTRESPONSE: (%.2f) Trigger resetting already-active event firing named: %s\n", gpGlobals->curtime, pResponse );
  59. }
  60. }
  61. }
  62. //-----------------------------------------------------------------------------
  63. // Purpose:
  64. //-----------------------------------------------------------------------------
  65. void CNPCEventResponseSystem::FrameUpdatePreEntityThink()
  66. {
  67. if ( !m_ActiveEvents.Count() || !AI_IsSinglePlayer() || !UTIL_GetLocalPlayer() )
  68. return;
  69. if ( m_flNextEventPoll > gpGlobals->curtime )
  70. return;
  71. m_flNextEventPoll = gpGlobals->curtime + 0.2;
  72. // Move through all events, removing expired ones and finding NPCs for active ones.
  73. for ( int i = m_ActiveEvents.First(); i != m_ActiveEvents.InvalidIndex(); )
  74. {
  75. float flTime = m_ActiveEvents[i].flEventTime;
  76. const char *pResponse = m_ActiveEvents.GetElementName(i);
  77. // Save off the next index so we can safely remove this one
  78. int iNext = m_ActiveEvents.Next(i);
  79. // Should it have expired by now?
  80. if ( !m_ActiveEvents[i].bPreventExpiration && (flTime + NPCEVENTRESPONSE_GIVEUP_TIME) < gpGlobals->curtime )
  81. {
  82. if ( ai_debug_eventresponses.GetBool() )
  83. {
  84. Msg( "NPCEVENTRESPONSE: (%.2f) Removing expired event named: %s\n", gpGlobals->curtime, pResponse );
  85. }
  86. m_ActiveEvents.RemoveAt(i);
  87. }
  88. else if ( m_ActiveEvents[i].flNextResponseTime < gpGlobals->curtime )
  89. {
  90. // If we've fired once, and our current event should expire now, then expire.
  91. if ( m_ActiveEvents[i].bPreventExpiration && (flTime + NPCEVENTRESPONSE_GIVEUP_TIME) < gpGlobals->curtime )
  92. {
  93. if ( ai_debug_eventresponses.GetBool() )
  94. {
  95. Msg( "NPCEVENTRESPONSE: (%.2f) Removing expired fired event named: %s\n", gpGlobals->curtime, pResponse );
  96. }
  97. m_ActiveEvents.RemoveAt(i);
  98. }
  99. else
  100. {
  101. float flNearestDist = NPCEVENTRESPONSE_DISTANCE_SQR;
  102. CAI_BaseNPC *pNearestNPC = NULL;
  103. Vector vecPlayerCenter = UTIL_GetLocalPlayer()->WorldSpaceCenter();
  104. // Try and find the nearest NPC to the player
  105. CAI_BaseNPC **ppAIs = g_AI_Manager.AccessAIs();
  106. for ( int j = 0; j < g_AI_Manager.NumAIs(); j++ )
  107. {
  108. if ( ppAIs[j]->CanRespondToEvent( pResponse ))
  109. {
  110. float flDistToPlayer = ( vecPlayerCenter - ppAIs[j]->WorldSpaceCenter()).LengthSqr();
  111. if ( flDistToPlayer < flNearestDist )
  112. {
  113. flNearestDist = flDistToPlayer;
  114. pNearestNPC = ppAIs[j];
  115. }
  116. }
  117. }
  118. // Found one?
  119. if ( pNearestNPC )
  120. {
  121. if ( pNearestNPC->RespondedTo( pResponse, m_ActiveEvents[i].bForce, m_ActiveEvents[i].bCancelScript ) )
  122. {
  123. // Don't remove the response yet. Leave it around until the refire time has expired.
  124. // This stops repeated firings of the same concept from spamming the NPCs.
  125. m_ActiveEvents[i].bPreventExpiration = true;
  126. m_ActiveEvents[i].flNextResponseTime = gpGlobals->curtime + NPCEVENTRESPONSE_REFIRE_TIME;
  127. if ( ai_debug_eventresponses.GetBool() )
  128. {
  129. Msg( "NPCEVENTRESPONSE: (%.2f) Event '%s' responded to by NPC '%s'. Refire available at: %.2f\n", gpGlobals->curtime, pResponse, pNearestNPC->GetDebugName(), m_ActiveEvents[i].flNextResponseTime );
  130. }
  131. // Don't issue multiple responses at once
  132. return;
  133. }
  134. }
  135. }
  136. }
  137. i = iNext;
  138. }
  139. }
  140. //---------------------------------------------------------------------------------------------
  141. // Entity version for mapmaker to hook into the system
  142. //---------------------------------------------------------------------------------------------
  143. class CNPCEventResponseSystemEntity : public CBaseEntity
  144. {
  145. DECLARE_CLASS( CNPCEventResponseSystemEntity, CBaseEntity );
  146. public:
  147. DECLARE_DATADESC();
  148. void Spawn();
  149. void InputTriggerResponseEvent( inputdata_t &inputdata );
  150. void InputForceTriggerResponseEvent( inputdata_t &inputdata );
  151. void InputForceTriggerResponseEventNoCancel( inputdata_t &inputdata );
  152. };
  153. LINK_ENTITY_TO_CLASS( ai_npc_eventresponsesystem, CNPCEventResponseSystemEntity );
  154. BEGIN_DATADESC( CNPCEventResponseSystemEntity )
  155. DEFINE_INPUTFUNC( FIELD_STRING, "TriggerResponseEvent", InputTriggerResponseEvent ),
  156. DEFINE_INPUTFUNC( FIELD_STRING, "ForceTriggerResponseEvent", InputForceTriggerResponseEvent ),
  157. DEFINE_INPUTFUNC( FIELD_STRING, "ForceTriggerResponseEventNoCancel", InputForceTriggerResponseEventNoCancel ),
  158. END_DATADESC()
  159. //-----------------------------------------------------------------------------
  160. // Purpose:
  161. //-----------------------------------------------------------------------------
  162. void CNPCEventResponseSystemEntity::Spawn( void )
  163. {
  164. // Invisible, non solid.
  165. AddSolidFlags( FSOLID_NOT_SOLID );
  166. AddEffects( EF_NODRAW );
  167. }
  168. //-----------------------------------------------------------------------------
  169. // Purpose:
  170. //-----------------------------------------------------------------------------
  171. void CNPCEventResponseSystemEntity::InputTriggerResponseEvent( inputdata_t &inputdata )
  172. {
  173. NPCEventResponse()->TriggerEvent( inputdata.value.String(), false, false );
  174. }
  175. //-----------------------------------------------------------------------------
  176. // Purpose:
  177. //-----------------------------------------------------------------------------
  178. void CNPCEventResponseSystemEntity::InputForceTriggerResponseEvent( inputdata_t &inputdata )
  179. {
  180. NPCEventResponse()->TriggerEvent( inputdata.value.String(), true, true );
  181. }
  182. //-----------------------------------------------------------------------------
  183. // Purpose:
  184. //-----------------------------------------------------------------------------
  185. void CNPCEventResponseSystemEntity::InputForceTriggerResponseEventNoCancel( inputdata_t &inputdata )
  186. {
  187. NPCEventResponse()->TriggerEvent( inputdata.value.String(), true, false );
  188. }