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.

1827 lines
59 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "sceneentity.h"
  8. #include "ai_playerally.h"
  9. #include "saverestore_utlmap.h"
  10. #include "eventqueue.h"
  11. #include "ai_behavior_lead.h"
  12. #include "gameinterface.h"
  13. // memdbgon must be the last include file in a .cpp file!!!
  14. #include "tier0/memdbgon.h"
  15. extern CServerGameDLL g_ServerGameDLL;
  16. extern ConVar rr_debugresponses;
  17. //-----------------------------------------------------------------------------
  18. ConVar sk_ally_regen_time( "sk_ally_regen_time", "0.3003", FCVAR_NONE, "Time taken for an ally to regenerate a point of health." );
  19. ConVar sv_npc_talker_maxdist( "sv_npc_talker_maxdist", "1024", 0, "NPCs over this distance from the player won't attempt to speak." );
  20. ConVar ai_no_talk_delay( "ai_no_talk_delay", "0" );
  21. ConVar rr_debug_qa( "rr_debug_qa", "0", FCVAR_NONE, "Set to 1 to see debug related to the Question & Answer system used to create conversations between allied NPCs.");
  22. //-----------------------------------------------------------------------------
  23. ConceptCategoryInfo_t g_ConceptCategoryInfos[] =
  24. {
  25. { 10, 20, 0, 0 },
  26. { 0, 0, 0, 0 },
  27. { 0, 0, 0, 0 },
  28. };
  29. ConceptInfo_t g_ConceptInfos[] =
  30. {
  31. { TLK_ANSWER, SPEECH_IMPORTANT, -1, -1, -1, -1, -1, -1, AICF_DEFAULT | AICF_ANSWER, },
  32. { TLK_ANSWER_HELLO, SPEECH_IMPORTANT, -1, -1, -1, -1, -1, -1, AICF_DEFAULT | AICF_ANSWER, },
  33. { TLK_QUESTION, SPEECH_IMPORTANT, -1, -1, -1, -1, -1, -1, AICF_DEFAULT | AICF_QUESTION, },
  34. { TLK_IDLE, SPEECH_IDLE, -1, -1, -1, -1, -1, -1, AICF_DEFAULT | AICF_TARGET_PLAYER, },
  35. { TLK_STARE, SPEECH_IDLE, -1, -1, -1, -1, 180, 0, AICF_DEFAULT | AICF_TARGET_PLAYER, },
  36. { TLK_LOOK, SPEECH_PRIORITY, -1, -1, -1, -1, -1, -1, AICF_DEFAULT | AICF_TARGET_PLAYER, },
  37. { TLK_HELLO, SPEECH_IDLE, 5, 10, -1, -1, -1, -1, AICF_DEFAULT | AICF_SPEAK_ONCE | AICF_PROPAGATE_SPOKEN | AICF_TARGET_PLAYER, },
  38. { TLK_PHELLO, SPEECH_IDLE, -1, -1, -1, -1, -1, -1, AICF_DEFAULT | AICF_SPEAK_ONCE | AICF_PROPAGATE_SPOKEN | AICF_TARGET_PLAYER, },
  39. { TLK_HELLO_NPC, SPEECH_IDLE, 5, 10, -1, -1, -1, -1, AICF_DEFAULT | AICF_QUESTION | AICF_SPEAK_ONCE, },
  40. { TLK_PIDLE, SPEECH_IDLE, -1, -1, -1, -1, -1, -1, AICF_DEFAULT, },
  41. { TLK_PQUESTION, SPEECH_IDLE, -1, -1, -1, -1, -1, -1, AICF_DEFAULT, },
  42. { TLK_SMELL, SPEECH_IDLE, -1, -1, -1, -1, -1, -1, AICF_DEFAULT, },
  43. { TLK_USE, SPEECH_IMPORTANT, -1, -1, -1, -1, -1, -1, AICF_DEFAULT, },
  44. { TLK_STARTFOLLOW, SPEECH_IMPORTANT, -1, -1, -1, -1, -1, -1, AICF_DEFAULT, },
  45. { TLK_STOPFOLLOW, SPEECH_IMPORTANT, -1, -1, -1, -1, -1, -1, AICF_DEFAULT, },
  46. { TLK_JOINPLAYER, SPEECH_IMPORTANT, -1, -1, -1, -1, -1, -1, AICF_DEFAULT, },
  47. { TLK_STOP, SPEECH_IMPORTANT, -1, -1, -1, -1, -1, -1, AICF_DEFAULT, },
  48. { TLK_NOSHOOT, SPEECH_IMPORTANT, -1, -1, -1, -1, -1, -1, AICF_DEFAULT, },
  49. { TLK_PLHURT1, SPEECH_IMPORTANT, -1, -1, -1, -1, -1, -1, AICF_DEFAULT, },
  50. { TLK_PLHURT2, SPEECH_IMPORTANT, -1, -1, -1, -1, -1, -1, AICF_DEFAULT, },
  51. { TLK_PLHURT3, SPEECH_IMPORTANT, -1, -1, -1, -1, -1, -1, AICF_DEFAULT, },
  52. { TLK_PLHURT, SPEECH_IMPORTANT, -1, -1, -1, -1, -1, -1, AICF_DEFAULT, },
  53. { TLK_PLPUSH, SPEECH_IMPORTANT, -1, -1, -1, -1, 15, 30, AICF_DEFAULT, },
  54. { TLK_PLRELOAD, SPEECH_IMPORTANT, -1, -1, -1, -1, 60, 0, AICF_DEFAULT, },
  55. { TLK_SHOT, SPEECH_IMPORTANT, -1, -1, -1, -1, -1, -1, AICF_DEFAULT, },
  56. { TLK_WOUND, SPEECH_IMPORTANT, -1, -1, -1, -1, -1, -1, AICF_DEFAULT, },
  57. { TLK_MORTAL, SPEECH_IMPORTANT, -1, -1, -1, -1, -1, -1, AICF_DEFAULT | AICF_SPEAK_ONCE, },
  58. { TLK_SEE_COMBINE, SPEECH_IMPORTANT, -1, -1, -1, -1, -1, -1, AICF_DEFAULT, },
  59. { TLK_ENEMY_DEAD, SPEECH_IMPORTANT, -1, -1, -1, -1, -1, -1, AICF_DEFAULT, },
  60. { TLK_ALYX_ENEMY_DEAD,SPEECH_IMPORTANT, -1, -1, -1, -1, -1, -1, AICF_DEFAULT, },
  61. { TLK_SELECTED, SPEECH_IMPORTANT, -1, -1, -1, -1, -1, -1, AICF_DEFAULT, },
  62. { TLK_COMMANDED, SPEECH_IMPORTANT, -1, -1, -1, -1, -1, -1, AICF_DEFAULT, },
  63. { TLK_COMMAND_FAILED, SPEECH_IMPORTANT, -1, -1, -1, -1, -1, -1, AICF_DEFAULT, },
  64. { TLK_DENY_COMMAND, SPEECH_IMPORTANT, -1, -1, -1, -1, -1, -1, AICF_DEFAULT, },
  65. { TLK_BETRAYED, SPEECH_IMPORTANT, -1, -1, -1, -1, -1, -1, AICF_DEFAULT, },
  66. { TLK_ALLY_KILLED, SPEECH_IMPORTANT, -1, -1, -1, -1, 15, 30, AICF_DEFAULT, },
  67. { TLK_ATTACKING, SPEECH_IMPORTANT, -1, -1, -1, -1, -1, -1, AICF_DEFAULT, },
  68. { TLK_HEAL, SPEECH_IMPORTANT, -1, -1, -1, -1, -1, -1, AICF_DEFAULT, },
  69. { TLK_GIVEAMMO, SPEECH_IMPORTANT, -1, -1, -1, -1, -1, -1, AICF_DEFAULT, },
  70. { TLK_HELP_ME, SPEECH_IMPORTANT, -1, -1, -1, -1, 7, 10, AICF_DEFAULT, },
  71. { TLK_PLYR_PHYSATK, SPEECH_IMPORTANT, -1, -1, -1, -1, -1, -1, AICF_DEFAULT, },
  72. { TLK_NEWWEAPON, SPEECH_IMPORTANT, -1, -1, -1, -1, -1, -1, AICF_DEFAULT, },
  73. { TLK_STARTCOMBAT, SPEECH_IMPORTANT, -1, -1, -1, -1, 30, 0, AICF_DEFAULT, },
  74. { TLK_WATCHOUT, SPEECH_IMPORTANT, -1, -1, -1, -1, 30, 45, AICF_DEFAULT, },
  75. { TLK_MOBBED, SPEECH_IMPORTANT, -1, -1, -1, -1, 10, 12, AICF_DEFAULT, },
  76. { TLK_MANY_ENEMIES, SPEECH_IMPORTANT, -1, -1, -1, -1, 45, 60, AICF_DEFAULT, },
  77. { TLK_DANGER, SPEECH_PRIORITY, -1, -1, -1, -1, 5, 7, AICF_DEFAULT, },
  78. { TLK_PLDEAD, SPEECH_PRIORITY, -1, -1, -1, -1, 100, 0, AICF_DEFAULT, },
  79. { TLK_HIDEANDRELOAD, SPEECH_PRIORITY, -1, -1, -1, -1, 45, 60, AICF_DEFAULT, },
  80. { TLK_FLASHLIGHT_ILLUM, SPEECH_PRIORITY,-1, -1, -1, -1, -1, -1, AICF_DEFAULT, },
  81. { TLK_FLASHLIGHT_ON, SPEECH_PRIORITY, -1, -1, -1, -1, -1, -1, AICF_DEFAULT, },
  82. { TLK_FLASHLIGHT_OFF, SPEECH_PRIORITY, -1, -1, -1, -1, -1, -1, AICF_DEFAULT, },
  83. { TLK_FOUNDPLAYER, SPEECH_IMPORTANT, -1, -1, -1, -1, -1, -1, AICF_TARGET_PLAYER, },
  84. { TLK_PLAYER_KILLED_NPC, SPEECH_IMPORTANT, -1, -1, -1, -1, -1, -1, AICF_DEFAULT, },
  85. { TLK_ENEMY_BURNING, SPEECH_IMPORTANT, -1, -1, -1, -1, -1, -1, AICF_DEFAULT, },
  86. { TLK_SPOTTED_ZOMBIE_WAKEUP, SPEECH_IMPORTANT, -1, -1, -1, -1, -1, -1, AICF_DEFAULT, },
  87. { TLK_SPOTTED_HEADCRAB_LEAVING_ZOMBIE,SPEECH_IMPORTANT, -1, -1, -1, -1, -1, -1, AICF_DEFAULT, },
  88. { TLK_DANGER_ZOMBINE_GRENADE, SPEECH_IMPORTANT, -1, -1, -1, -1, -1, -1, AICF_DEFAULT, },
  89. { TLK_BALLSOCKETED, SPEECH_IMPORTANT, -1, -1, -1, -1, -1, -1, AICF_DEFAULT, },
  90. // Darkness mode
  91. { TLK_DARKNESS_LOSTPLAYER, SPEECH_IMPORTANT,-1, -1, -1, -1, -1, -1, AICF_DEFAULT, },
  92. { TLK_DARKNESS_FOUNDPLAYER, SPEECH_IMPORTANT,-1, -1, -1, -1, -1, -1, AICF_DEFAULT, },
  93. { TLK_DARKNESS_UNKNOWN_WOUND, SPEECH_IMPORTANT,-1,-1, -1, -1, -1, -1, AICF_DEFAULT, },
  94. { TLK_DARKNESS_HEARDSOUND, SPEECH_IMPORTANT,-1, -1, -1, -1, 20, 30, AICF_DEFAULT, },
  95. { TLK_DARKNESS_LOSTENEMY_BY_FLASHLIGHT, SPEECH_IMPORTANT,-1, -1, -1, -1, -1, -1, AICF_DEFAULT, },
  96. { TLK_DARKNESS_LOSTENEMY_BY_FLASHLIGHT_EXPIRED, SPEECH_IMPORTANT,-1, -1, -1, -1, -1, -1, AICF_DEFAULT, },
  97. { TLK_DARKNESS_FOUNDENEMY_BY_FLASHLIGHT, SPEECH_IMPORTANT,-1, -1, -1, -1, -1, -1, AICF_DEFAULT, },
  98. { TLK_DARKNESS_FLASHLIGHT_EXPIRED, SPEECH_IMPORTANT, -1, -1, -1, -1, -1, -1, AICF_DEFAULT, },
  99. { TLK_SPOTTED_INCOMING_HEADCRAB, SPEECH_IMPORTANT,-1, -1, -1, -1, -1, -1, AICF_DEFAULT, },
  100. // Lead behaviour
  101. { TLK_LEAD_START, SPEECH_IMPORTANT,-1, -1, -1, -1, -1, -1, AICF_DEFAULT, },
  102. { TLK_LEAD_ARRIVAL, SPEECH_IMPORTANT,-1, -1, -1, -1, -1, -1, AICF_DEFAULT, },
  103. { TLK_LEAD_SUCCESS, SPEECH_IMPORTANT,-1, -1, -1, -1, -1, -1, AICF_DEFAULT, },
  104. { TLK_LEAD_FAILURE, SPEECH_IMPORTANT,-1, -1, -1, -1, -1, -1, AICF_DEFAULT, },
  105. { TLK_LEAD_COMINGBACK,SPEECH_IMPORTANT,-1, -1, -1, -1, -1, -1, AICF_DEFAULT, },
  106. { TLK_LEAD_CATCHUP, SPEECH_IMPORTANT,-1, -1, -1, -1, -1, -1, AICF_DEFAULT, },
  107. { TLK_LEAD_RETRIEVE, SPEECH_IMPORTANT,-1, -1, -1, -1, -1, -1, AICF_DEFAULT, },
  108. { TLK_LEAD_ATTRACTPLAYER, SPEECH_IMPORTANT,-1,-1, -1, -1, -1, -1, AICF_DEFAULT, },
  109. { TLK_LEAD_WAITOVER, SPEECH_IMPORTANT,-1, -1, -1, -1, -1, -1, AICF_DEFAULT, },
  110. { TLK_LEAD_MISSINGWEAPON, SPEECH_IMPORTANT,-1,-1, -1, -1, -1, -1, AICF_DEFAULT, },
  111. { TLK_LEAD_IDLE, SPEECH_IMPORTANT,-1, -1, -1, -1, -1, -1, AICF_DEFAULT, },
  112. // Passenger behaviour
  113. { TLK_PASSENGER_NEW_RADAR_CONTACT, SPEECH_IMPORTANT, -1, -1, -1, -1, -1, -1, AICF_DEFAULT, },
  114. };
  115. //-----------------------------------------------------------------------------
  116. bool ConceptStringLessFunc( const string_t &lhs, const string_t &rhs )
  117. {
  118. return CaselessStringLessThan( STRING(lhs), STRING(rhs) );
  119. }
  120. //-----------------------------------------------------------------------------
  121. #if AI_CONCEPTS_ARE_STRINGS
  122. class CConceptInfoMap : public CUtlMap<AIConcept_t, ConceptInfo_t *> {
  123. public:
  124. CConceptInfoMap() :
  125. CUtlMap<AIConcept_t, ConceptInfo_t *>( CaselessStringLessThan )
  126. {
  127. for ( int i = 0; i < ARRAYSIZE(g_ConceptInfos); i++ )
  128. {
  129. Insert( g_ConceptInfos[i].concept, &g_ConceptInfos[i] );
  130. }
  131. }
  132. };
  133. #else
  134. bool ConceptIDLessFunc( const AIConcept_t::tGenericId &lhs, const AIConcept_t::tGenericId &rhs )
  135. {
  136. return CaselessStringLessThan( CAI_Concept::GetStringForGenericId(lhs), CAI_Concept::GetStringForGenericId(rhs) );
  137. }
  138. class CConceptInfoMap : public CUtlMap<AIConcept_t::tGenericId, ConceptInfo_t *> {
  139. public:
  140. CConceptInfoMap() :
  141. CUtlMap<AIConcept_t::tGenericId, ConceptInfo_t *>( ConceptIDLessFunc )
  142. {
  143. for ( int i = 0; i < ARRAYSIZE(g_ConceptInfos); i++ )
  144. {
  145. Insert( g_ConceptInfos[i].concept, &g_ConceptInfos[i] );
  146. }
  147. }
  148. };
  149. #endif
  150. static CConceptInfoMap g_ConceptInfoMap;
  151. CAI_AllySpeechManager::CAI_AllySpeechManager()
  152. {
  153. m_ConceptTimers.SetLessFunc( ConceptStringLessFunc );
  154. Assert( !gm_pSpeechManager );
  155. gm_pSpeechManager = this;
  156. }
  157. CAI_AllySpeechManager::~CAI_AllySpeechManager()
  158. {
  159. gm_pSpeechManager = NULL;
  160. }
  161. void CAI_AllySpeechManager::Spawn()
  162. {
  163. Assert( g_ConceptInfoMap.Count() != 0 );
  164. for ( int i = 0; i < ARRAYSIZE(g_ConceptInfos); i++ )
  165. m_ConceptTimers.Insert( AllocPooledString( g_ConceptInfos[i].concept ), CSimpleSimTimer() );
  166. }
  167. void CAI_AllySpeechManager::AddCustomConcept( const ConceptInfo_t &conceptInfo )
  168. {
  169. Assert( g_ConceptInfoMap.Count() != 0 );
  170. Assert( m_ConceptTimers.Count() != 0 );
  171. if ( g_ConceptInfoMap.Find( conceptInfo.concept ) == g_ConceptInfoMap.InvalidIndex() )
  172. {
  173. g_ConceptInfoMap.Insert( conceptInfo.concept, new ConceptInfo_t( conceptInfo ) );
  174. }
  175. if ( m_ConceptTimers.Find( AllocPooledString(conceptInfo.concept) ) == m_ConceptTimers.InvalidIndex() )
  176. {
  177. m_ConceptTimers.Insert( AllocPooledString( conceptInfo.concept ), CSimpleSimTimer() );
  178. }
  179. }
  180. ConceptCategoryInfo_t *CAI_AllySpeechManager::GetConceptCategoryInfo( ConceptCategory_t category )
  181. {
  182. return &g_ConceptCategoryInfos[category];
  183. }
  184. ConceptInfo_t *CAI_AllySpeechManager::GetConceptInfo( AIConcept_t concept )
  185. {
  186. int iResult = g_ConceptInfoMap.Find( concept );
  187. return ( iResult != g_ConceptInfoMap.InvalidIndex() ) ? g_ConceptInfoMap[iResult] : NULL;
  188. }
  189. void CAI_AllySpeechManager::OnSpokeConcept( CAI_PlayerAlly *pPlayerAlly, AIConcept_t concept, AI_Response *response )
  190. {
  191. ConceptInfo_t * pConceptInfo = GetConceptInfo( concept );
  192. ConceptCategory_t category = ( pConceptInfo ) ? pConceptInfo->category : SPEECH_IDLE;
  193. ConceptCategoryInfo_t * pCategoryInfo = GetConceptCategoryInfo( category );
  194. if ( pConceptInfo )
  195. {
  196. if ( pConceptInfo->flags & AICF_PROPAGATE_SPOKEN )
  197. {
  198. CAI_BaseNPC **ppAIs = g_AI_Manager.AccessAIs();
  199. CAI_PlayerAlly *pTalker;
  200. for ( int i = 0; i < g_AI_Manager.NumAIs(); i++ )
  201. {
  202. pTalker = dynamic_cast<CAI_PlayerAlly *>(ppAIs[i]);
  203. if ( pTalker && pTalker != pPlayerAlly &&
  204. (pTalker->GetAbsOrigin() - pPlayerAlly->GetAbsOrigin()).LengthSqr() < Square(TALKRANGE_MIN * 2) &&
  205. pPlayerAlly->FVisible( pTalker ) )
  206. {
  207. // Tell this guy he's already said the concept to the player, too.
  208. pTalker->GetExpresser()->SetSpokeConcept( concept, NULL, false );
  209. }
  210. }
  211. }
  212. }
  213. if ( !ai_no_talk_delay.GetBool() )
  214. {
  215. if ( pConceptInfo && pConceptInfo->minGlobalCategoryDelay != -1 )
  216. {
  217. Assert( pConceptInfo->maxGlobalCategoryDelay != -1 );
  218. SetCategoryDelay( pConceptInfo->category, pConceptInfo->minGlobalCategoryDelay, pConceptInfo->maxGlobalCategoryDelay );
  219. }
  220. else if ( pCategoryInfo->maxGlobalDelay > 0 )
  221. {
  222. SetCategoryDelay( category, pCategoryInfo->minGlobalDelay, pCategoryInfo->maxGlobalDelay );
  223. }
  224. if ( pConceptInfo && pConceptInfo->minPersonalCategoryDelay != -1 )
  225. {
  226. Assert( pConceptInfo->maxPersonalCategoryDelay != -1 );
  227. pPlayerAlly->SetCategoryDelay( pConceptInfo->category, pConceptInfo->minPersonalCategoryDelay, pConceptInfo->maxPersonalCategoryDelay );
  228. }
  229. else if ( pCategoryInfo->maxPersonalDelay > 0 )
  230. {
  231. pPlayerAlly->SetCategoryDelay( category, pCategoryInfo->minPersonalDelay, pCategoryInfo->maxPersonalDelay );
  232. }
  233. if ( pConceptInfo && pConceptInfo->minConceptDelay != -1 )
  234. {
  235. Assert( pConceptInfo->maxConceptDelay != -1 );
  236. char iConceptTimer = m_ConceptTimers.Find( MAKE_STRING(concept) );
  237. if ( iConceptTimer != m_ConceptTimers.InvalidIndex() )
  238. m_ConceptTimers[iConceptTimer].Set( pConceptInfo->minConceptDelay, pConceptInfo->minConceptDelay );
  239. }
  240. }
  241. }
  242. void CAI_AllySpeechManager::SetCategoryDelay( ConceptCategory_t category, float minDelay, float maxDelay )
  243. {
  244. if ( category != SPEECH_PRIORITY )
  245. m_ConceptCategoryTimers[category].Set( minDelay, maxDelay );
  246. }
  247. bool CAI_AllySpeechManager::CategoryDelayExpired( ConceptCategory_t category )
  248. {
  249. if ( category == SPEECH_PRIORITY )
  250. return true;
  251. return m_ConceptCategoryTimers[category].Expired();
  252. }
  253. bool CAI_AllySpeechManager::ConceptDelayExpired( AIConcept_t concept )
  254. {
  255. char iConceptTimer = m_ConceptTimers.Find( MAKE_STRING(concept) );
  256. if ( iConceptTimer != m_ConceptTimers.InvalidIndex() )
  257. return m_ConceptTimers[iConceptTimer].Expired();
  258. return true;
  259. }
  260. //-----------------------------------------------------------------------------
  261. LINK_ENTITY_TO_CLASS( ai_ally_speech_manager, CAI_AllySpeechManager );
  262. BEGIN_DATADESC( CAI_AllySpeechManager )
  263. DEFINE_EMBEDDED_AUTO_ARRAY(m_ConceptCategoryTimers),
  264. DEFINE_UTLMAP( m_ConceptTimers, FIELD_STRING, FIELD_EMBEDDED ),
  265. END_DATADESC()
  266. //-----------------------------------------------------------------------------
  267. CAI_AllySpeechManager *CAI_AllySpeechManager::gm_pSpeechManager;
  268. //-----------------------------------------------------------------------------
  269. CAI_AllySpeechManager *GetAllySpeechManager()
  270. {
  271. if ( !CAI_AllySpeechManager::gm_pSpeechManager )
  272. {
  273. CreateEntityByName( "ai_ally_speech_manager" );
  274. Assert( CAI_AllySpeechManager::gm_pSpeechManager );
  275. if ( CAI_AllySpeechManager::gm_pSpeechManager )
  276. DispatchSpawn( CAI_AllySpeechManager::gm_pSpeechManager );
  277. }
  278. return CAI_AllySpeechManager::gm_pSpeechManager;
  279. }
  280. //-----------------------------------------------------------------------------
  281. //
  282. // CLASS: CAI_PlayerAlly
  283. //
  284. //-----------------------------------------------------------------------------
  285. BEGIN_DATADESC( CAI_PlayerAlly )
  286. DEFINE_EMBEDDED( m_PendingResponse ),
  287. DEFINE_STDSTRING( m_PendingConcept ),
  288. DEFINE_FIELD( m_TimePendingSet, FIELD_TIME ),
  289. DEFINE_FIELD( m_hTalkTarget, FIELD_EHANDLE ),
  290. DEFINE_FIELD( m_flNextRegenTime, FIELD_TIME ),
  291. DEFINE_FIELD( m_flTimePlayerStartStare, FIELD_TIME ),
  292. DEFINE_FIELD( m_hPotentialSpeechTarget, FIELD_EHANDLE ),
  293. DEFINE_FIELD( m_flNextIdleSpeechTime, FIELD_TIME ),
  294. DEFINE_FIELD( m_iQARandomNumber, FIELD_INTEGER ),
  295. DEFINE_FIELD( m_hSpeechFilter, FIELD_EHANDLE ),
  296. DEFINE_EMBEDDED_AUTO_ARRAY(m_ConceptCategoryTimers),
  297. DEFINE_KEYFIELD( m_bGameEndAlly, FIELD_BOOLEAN, "GameEndAlly" ),
  298. DEFINE_FIELD( m_bCanSpeakWhileScripting, FIELD_BOOLEAN ),
  299. DEFINE_FIELD( m_flHealthAccumulator, FIELD_FLOAT ),
  300. DEFINE_FIELD( m_flTimeLastRegen, FIELD_TIME ),
  301. // Inputs
  302. DEFINE_INPUTFUNC( FIELD_VOID, "IdleRespond", InputIdleRespond ),
  303. DEFINE_INPUTFUNC( FIELD_STRING, "SpeakResponseConcept", InputSpeakResponseConcept ),
  304. DEFINE_INPUTFUNC( FIELD_VOID, "MakeGameEndAlly", InputMakeGameEndAlly ),
  305. DEFINE_INPUTFUNC( FIELD_VOID, "MakeRegularAlly", InputMakeRegularAlly ),
  306. DEFINE_INPUTFUNC( FIELD_INTEGER, "AnswerQuestion", InputAnswerQuestion ),
  307. DEFINE_INPUTFUNC( FIELD_INTEGER, "AnswerQuestionHello", InputAnswerQuestionHello ),
  308. DEFINE_INPUTFUNC( FIELD_VOID, "EnableSpeakWhileScripting", InputEnableSpeakWhileScripting ),
  309. DEFINE_INPUTFUNC( FIELD_VOID, "DisableSpeakWhileScripting", InputDisableSpeakWhileScripting ),
  310. END_DATADESC()
  311. CBaseEntity *CreatePlayerLoadSave( Vector vOrigin, float flDuration, float flHoldTime, float flLoadTime );
  312. ConVar npc_ally_deathmessage( "npc_ally_deathmessage", "1", FCVAR_CHEAT );
  313. void CAI_PlayerAlly::InputMakeGameEndAlly( inputdata_t &inputdata )
  314. {
  315. m_bGameEndAlly = true;
  316. }
  317. void CAI_PlayerAlly::InputMakeRegularAlly( inputdata_t &inputdata )
  318. {
  319. m_bGameEndAlly = false;
  320. }
  321. void CAI_PlayerAlly::DisplayDeathMessage( void )
  322. {
  323. if ( m_bGameEndAlly == false )
  324. return;
  325. if ( npc_ally_deathmessage.GetBool() == 0 )
  326. return;
  327. CBaseEntity *pPlayer = AI_GetSinglePlayer();
  328. if ( pPlayer )
  329. {
  330. UTIL_ShowMessage( GetDeathMessageText(), ToBasePlayer( pPlayer ) );
  331. ToBasePlayer(pPlayer)->NotifySinglePlayerGameEnding();
  332. }
  333. CBaseEntity *pReload = CreatePlayerLoadSave( GetAbsOrigin(), 1.5f, 8.0f, 4.5f );
  334. if ( pReload )
  335. {
  336. pReload->SetRenderColor( 0, 0, 0 );
  337. pReload->SetRenderAlpha( 255 );
  338. g_EventQueue.AddEvent( pReload, "Reload", 1.5f, pReload, pReload );
  339. }
  340. // clear any pending autosavedangerous
  341. g_ServerGameDLL.m_fAutoSaveDangerousTime = 0.0f;
  342. g_ServerGameDLL.m_fAutoSaveDangerousMinHealthToCommit = 0.0f;
  343. }
  344. //-----------------------------------------------------------------------------
  345. // NPCs derived from CAI_PlayerAlly should call this in precache()
  346. //-----------------------------------------------------------------------------
  347. void CAI_PlayerAlly::TalkInit( void )
  348. {
  349. }
  350. //-----------------------------------------------------------------------------
  351. //-----------------------------------------------------------------------------
  352. void CAI_PlayerAlly::GatherConditions( void )
  353. {
  354. BaseClass::GatherConditions();
  355. if ( !HasCondition( COND_SEE_PLAYER ) )
  356. {
  357. SetCondition( COND_TALKER_CLIENTUNSEEN );
  358. }
  359. CBasePlayer *pLocalPlayer = AI_GetSinglePlayer();
  360. if ( !pLocalPlayer )
  361. {
  362. if ( AI_IsSinglePlayer() )
  363. SetCondition( COND_TALKER_PLAYER_DEAD );
  364. return;
  365. }
  366. if ( !pLocalPlayer->IsAlive() )
  367. {
  368. SetCondition( COND_TALKER_PLAYER_DEAD );
  369. }
  370. if ( HasCondition( COND_SEE_PLAYER ) )
  371. {
  372. bool bPlayerIsLooking = false;
  373. if ( ( pLocalPlayer->GetAbsOrigin() - GetAbsOrigin() ).Length2DSqr() < Square(TALKER_STARE_DIST) )
  374. {
  375. if ( pLocalPlayer->FInViewCone( EyePosition() ) )
  376. {
  377. if ( pLocalPlayer->GetSmoothedVelocity().LengthSqr() < Square( 100 ) )
  378. bPlayerIsLooking = true;
  379. }
  380. }
  381. if ( bPlayerIsLooking )
  382. {
  383. SetCondition( COND_TALKER_PLAYER_STARING );
  384. if ( m_flTimePlayerStartStare == 0 )
  385. m_flTimePlayerStartStare = gpGlobals->curtime;
  386. }
  387. else
  388. {
  389. m_flTimePlayerStartStare = 0;
  390. ClearCondition( COND_TALKER_PLAYER_STARING );
  391. }
  392. }
  393. }
  394. //-----------------------------------------------------------------------------
  395. //-----------------------------------------------------------------------------
  396. void CAI_PlayerAlly::GatherEnemyConditions( CBaseEntity *pEnemy )
  397. {
  398. BaseClass::GatherEnemyConditions( pEnemy );
  399. if ( GetLastEnemyTime() == 0 || gpGlobals->curtime - GetLastEnemyTime() > 30 )
  400. {
  401. #ifdef HL2_DLL
  402. if ( HasCondition( COND_SEE_ENEMY ) && ( pEnemy->Classify() != CLASS_BULLSEYE ) )
  403. {
  404. if( Classify() == CLASS_PLAYER_ALLY_VITAL && hl2_episodic.GetBool() )
  405. {
  406. CBasePlayer *pPlayer = AI_GetSinglePlayer();
  407. if( pPlayer )
  408. {
  409. // If I can see the player, and the player would see this enemy if he turned around...
  410. if( !pPlayer->FInViewCone(pEnemy) && FVisible( pPlayer ) && pPlayer->FVisible(pEnemy) )
  411. {
  412. Vector2D vecPlayerView = pPlayer->EyeDirection2D().AsVector2D();
  413. Vector2D vecToEnemy = ( pEnemy->GetAbsOrigin() - pPlayer->GetAbsOrigin() ).AsVector2D();
  414. Vector2DNormalize( vecToEnemy );
  415. if( DotProduct2D(vecPlayerView, vecToEnemy) <= -0.75 )
  416. {
  417. SpeakIfAllowed( TLK_WATCHOUT, "dangerloc:behind" );
  418. return;
  419. }
  420. }
  421. }
  422. }
  423. SpeakIfAllowed( TLK_STARTCOMBAT );
  424. }
  425. #else
  426. if ( HasCondition( COND_SEE_ENEMY ) )
  427. {
  428. SpeakIfAllowed( TLK_STARTCOMBAT );
  429. }
  430. #endif //HL2_DLL
  431. }
  432. }
  433. //-----------------------------------------------------------------------------
  434. //-----------------------------------------------------------------------------
  435. void CAI_PlayerAlly::OnStateChange( NPC_STATE OldState, NPC_STATE NewState )
  436. {
  437. BaseClass::OnStateChange( OldState, NewState );
  438. if ( OldState == NPC_STATE_COMBAT )
  439. {
  440. DeferAllIdleSpeech();
  441. }
  442. if ( GetState() == NPC_STATE_IDLE || GetState() == NPC_STATE_ALERT )
  443. {
  444. m_flNextIdleSpeechTime = gpGlobals->curtime + RandomFloat(5,10);
  445. }
  446. else
  447. {
  448. m_flNextIdleSpeechTime = 0;
  449. }
  450. }
  451. //-----------------------------------------------------------------------------
  452. //-----------------------------------------------------------------------------
  453. void CAI_PlayerAlly::PrescheduleThink( void )
  454. {
  455. BaseClass::PrescheduleThink();
  456. #ifdef HL2_DLL
  457. // Vital allies regenerate
  458. if( GetHealth() >= GetMaxHealth() )
  459. {
  460. // Avoid huge deltas on first regeneration of health after long period of time at full health.
  461. m_flTimeLastRegen = gpGlobals->curtime;
  462. }
  463. else if ( ShouldRegenerateHealth() )
  464. {
  465. float flDelta = gpGlobals->curtime - m_flTimeLastRegen;
  466. float flHealthPerSecond = 1.0f / sk_ally_regen_time.GetFloat();
  467. float flHealthRegen = flHealthPerSecond * flDelta;
  468. if ( g_pGameRules->IsSkillLevel(SKILL_HARD) )
  469. flHealthRegen *= 0.5f;
  470. else if ( g_pGameRules->IsSkillLevel(SKILL_EASY) )
  471. flHealthRegen *= 1.5f;
  472. m_flTimeLastRegen = gpGlobals->curtime;
  473. TakeHealth( flHealthRegen, DMG_GENERIC );
  474. }
  475. #ifdef HL2_EPISODIC
  476. if ( (GetState() == NPC_STATE_IDLE || GetState() == NPC_STATE_ALERT)
  477. && !HasCondition(COND_RECEIVED_ORDERS) && !IsInAScript() )
  478. {
  479. if ( m_flNextIdleSpeechTime && m_flNextIdleSpeechTime < gpGlobals->curtime )
  480. {
  481. AISpeechSelection_t selection;
  482. if ( SelectNonCombatSpeech( &selection ) )
  483. {
  484. SetSpeechTarget( selection.hSpeechTarget );
  485. SpeakDispatchResponse( selection.concept.c_str(), &selection.response );
  486. m_flNextIdleSpeechTime = gpGlobals->curtime + RandomFloat( 20,30 );
  487. }
  488. else
  489. {
  490. m_flNextIdleSpeechTime = gpGlobals->curtime + RandomFloat( 10,20 );
  491. }
  492. }
  493. }
  494. #endif // HL2_EPISODIC
  495. #endif // HL2_DLL
  496. }
  497. //-----------------------------------------------------------------------------
  498. //-----------------------------------------------------------------------------
  499. int CAI_PlayerAlly::SelectSchedule( void )
  500. {
  501. if ( !HasCondition(COND_RECEIVED_ORDERS) )
  502. {
  503. // sustained light wounds?
  504. if ( m_iHealth <= m_iMaxHealth * 0.75 && IsAllowedToSpeak( TLK_WOUND ) && !GetExpresser()->SpokeConcept(TLK_WOUND) )
  505. {
  506. CTakeDamageInfo info;
  507. PainSound( info );
  508. }
  509. // sustained heavy wounds?
  510. else if ( m_iHealth <= m_iMaxHealth * 0.5 && IsAllowedToSpeak( TLK_MORTAL) )
  511. {
  512. Speak( TLK_MORTAL );
  513. }
  514. }
  515. return BaseClass::SelectSchedule();
  516. }
  517. //-----------------------------------------------------------------------------
  518. //-----------------------------------------------------------------------------
  519. bool CAI_PlayerAlly::SelectSpeechResponse( AIConcept_t concept, const char *pszModifiers, CBaseEntity *pTarget, AISpeechSelection_t *pSelection )
  520. {
  521. if ( IsAllowedToSpeak( concept ) )
  522. {
  523. AI_CriteriaSet criteria;
  524. GatherCriteria(&criteria, concept, pszModifiers);
  525. if ( FindResponse(pSelection->response, concept, &criteria) )
  526. {
  527. pSelection->Set(concept, pTarget);
  528. return true;
  529. }
  530. /*
  531. AI_Response response;
  532. if ( FindResponse( response, concept, &criteria ) )
  533. {
  534. pSelection->Set( concept, &response, pTarget );
  535. return true;
  536. }
  537. */
  538. }
  539. return false;
  540. }
  541. //-----------------------------------------------------------------------------
  542. //-----------------------------------------------------------------------------
  543. void CAI_PlayerAlly::SetPendingSpeech( AIConcept_t concept, AI_Response *pResponse )
  544. {
  545. m_PendingResponse = *pResponse;
  546. // pResponse->Release();
  547. m_PendingConcept = concept;
  548. m_TimePendingSet = gpGlobals->curtime;
  549. }
  550. //-----------------------------------------------------------------------------
  551. //-----------------------------------------------------------------------------
  552. void CAI_PlayerAlly::ClearPendingSpeech()
  553. {
  554. m_PendingConcept.erase();
  555. m_TimePendingSet = 0;
  556. }
  557. //-----------------------------------------------------------------------------
  558. //-----------------------------------------------------------------------------
  559. bool CAI_PlayerAlly::SelectIdleSpeech( AISpeechSelection_t *pSelection )
  560. {
  561. if ( !IsOkToSpeak( SPEECH_IDLE ) )
  562. return false;
  563. CBasePlayer *pTarget = assert_cast<CBasePlayer *>(FindSpeechTarget( AIST_PLAYERS | AIST_FACING_TARGET ));
  564. if ( pTarget )
  565. {
  566. if ( SelectSpeechResponse( TLK_HELLO, NULL, pTarget, pSelection ) )
  567. return true;
  568. if ( GetTimePlayerStaring() > 6 && !IsMoving() )
  569. {
  570. if ( SelectSpeechResponse( TLK_STARE, NULL, pTarget, pSelection ) )
  571. return true;
  572. }
  573. int chance = ( IsMoving() ) ? 20 : 2;
  574. if ( ShouldSpeakRandom( TLK_IDLE, chance ) && SelectSpeechResponse( TLK_IDLE, NULL, pTarget, pSelection ) )
  575. return true;
  576. }
  577. return false;
  578. }
  579. //-----------------------------------------------------------------------------
  580. // Purpose:
  581. //-----------------------------------------------------------------------------
  582. bool CAI_PlayerAlly::SelectAlertSpeech( AISpeechSelection_t *pSelection )
  583. {
  584. #ifdef HL2_EPISODIC
  585. CBasePlayer *pTarget = assert_cast<CBasePlayer *>(FindSpeechTarget( AIST_PLAYERS | AIST_FACING_TARGET ));
  586. if ( pTarget )
  587. {
  588. if ( pTarget->IsAlive() )
  589. {
  590. float flHealthPerc = ((float)pTarget->m_iHealth / (float)pTarget->m_iMaxHealth);
  591. if ( flHealthPerc < 1.0 )
  592. {
  593. if ( SelectSpeechResponse( TLK_PLHURT, NULL, pTarget, pSelection ) )
  594. return true;
  595. }
  596. }
  597. }
  598. #endif
  599. return SelectIdleSpeech( pSelection );
  600. }
  601. //-----------------------------------------------------------------------------
  602. //-----------------------------------------------------------------------------
  603. bool CAI_PlayerAlly::SelectInterjection()
  604. {
  605. if ( HasPendingSpeech() )
  606. return false;
  607. if ( HasCondition(COND_RECEIVED_ORDERS) )
  608. return false;
  609. if ( GetState() == NPC_STATE_IDLE || GetState() == NPC_STATE_ALERT )
  610. {
  611. AISpeechSelection_t selection;
  612. if ( SelectIdleSpeech( &selection ) )
  613. {
  614. SetSpeechTarget( selection.hSpeechTarget );
  615. SpeakDispatchResponse( selection.concept.c_str(), &selection.response, NULL );
  616. return true;
  617. }
  618. }
  619. return false;
  620. }
  621. //-----------------------------------------------------------------------------
  622. //-----------------------------------------------------------------------------
  623. bool CAI_PlayerAlly::SelectPlayerUseSpeech()
  624. {
  625. if( IsOkToSpeakInResponseToPlayer() )
  626. {
  627. if ( Speak( TLK_USE ) )
  628. DeferAllIdleSpeech();
  629. else
  630. return Speak( ( !GetExpresser()->SpokeConcept( TLK_HELLO ) ) ? TLK_HELLO : TLK_IDLE );
  631. }
  632. return false;
  633. }
  634. //-----------------------------------------------------------------------------
  635. // Purpose:
  636. //-----------------------------------------------------------------------------
  637. bool CAI_PlayerAlly::SelectQuestionAndAnswerSpeech( AISpeechSelection_t *pSelection )
  638. {
  639. if ( !IsOkToSpeak( SPEECH_IDLE ) )
  640. return false;
  641. if ( IsMoving() )
  642. return false;
  643. // if there is a friend nearby to speak to, play sentence, set friend's response time, return
  644. CAI_PlayerAlly *pFriend = dynamic_cast<CAI_PlayerAlly *>(FindSpeechTarget( AIST_NPCS ));
  645. if ( pFriend && !pFriend->IsMoving() && !pFriend->HasSpawnFlags(SF_NPC_GAG) )
  646. return SelectQuestionFriend( pFriend, pSelection );
  647. return false;
  648. }
  649. //-----------------------------------------------------------------------------
  650. // Purpose:
  651. //-----------------------------------------------------------------------------
  652. void CAI_PlayerAlly::PostSpeakDispatchResponse( AIConcept_t concept, AI_Response *response )
  653. {
  654. #ifdef HL2_EPISODIC
  655. CAI_AllySpeechManager *pSpeechManager = GetAllySpeechManager();
  656. ConceptInfo_t *pConceptInfo = pSpeechManager->GetConceptInfo( concept );
  657. if ( pConceptInfo && (pConceptInfo->flags & AICF_QUESTION) && GetSpeechTarget() )
  658. {
  659. bool bSaidHelloToNPC = !Q_strcmp(concept, "TLK_HELLO_NPC");
  660. float duration = GetExpresser()->GetSemaphoreAvailableTime(this) - gpGlobals->curtime;
  661. if ( rr_debug_qa.GetBool() )
  662. {
  663. if ( bSaidHelloToNPC )
  664. {
  665. Warning("Q&A: '%s' said Hello to '%s' (concept %s)\n", GetDebugName(), GetSpeechTarget()->GetDebugName(), concept );
  666. }
  667. else
  668. {
  669. Warning("Q&A: '%s' questioned '%s' (concept %s)\n", GetDebugName(), GetSpeechTarget()->GetDebugName(), concept );
  670. }
  671. NDebugOverlay::HorzArrow( GetAbsOrigin(), GetSpeechTarget()->GetAbsOrigin(), 8, 0, 255, 0, 64, true, duration );
  672. }
  673. // If we spoke a Question, tell our friend to answer
  674. const char *pszInput;
  675. if ( bSaidHelloToNPC )
  676. {
  677. pszInput = "AnswerQuestionHello";
  678. }
  679. else
  680. {
  681. pszInput = "AnswerQuestion";
  682. }
  683. // Set the input parameter to the random number we used to find the Question
  684. variant_t value;
  685. value.SetInt( m_iQARandomNumber );
  686. g_EventQueue.AddEvent( GetSpeechTarget(), pszInput, value, duration + .2, this, this );
  687. if ( GetSpeechTarget()->MyNPCPointer() )
  688. {
  689. AddLookTarget( GetSpeechTarget()->MyNPCPointer(), 1.0, duration + random->RandomFloat( 0.4, 1.2 ), 0.5 );
  690. GetSpeechTarget()->MyNPCPointer()->AddLookTarget( this, 1.0, duration + random->RandomFloat( 0.4, 1 ), 0.7 );
  691. }
  692. // Don't let anyone else butt in.
  693. DeferAllIdleSpeech( random->RandomFloat( TALKER_DEFER_IDLE_SPEAK_MIN, TALKER_DEFER_IDLE_SPEAK_MAX ), GetSpeechTarget()->MyNPCPointer() );
  694. }
  695. else if ( pConceptInfo && (pConceptInfo->flags & AICF_ANSWER) && GetSpeechTarget() )
  696. {
  697. float duration = GetExpresser()->GetSemaphoreAvailableTime(this) - gpGlobals->curtime;
  698. if ( rr_debug_qa.GetBool() )
  699. {
  700. NDebugOverlay::HorzArrow( GetAbsOrigin(), GetSpeechTarget()->GetAbsOrigin(), 8, 0, 255, 0, 64, true, duration );
  701. }
  702. if ( GetSpeechTarget()->MyNPCPointer() )
  703. {
  704. AddLookTarget( GetSpeechTarget()->MyNPCPointer(), 1.0, duration + random->RandomFloat( 0, 0.3 ), 0.5 );
  705. GetSpeechTarget()->MyNPCPointer()->AddLookTarget( this, 1.0, duration + random->RandomFloat( 0.2, 0.5 ), 0.7 );
  706. }
  707. }
  708. m_hPotentialSpeechTarget = NULL;
  709. #endif // HL2_EPISODIC
  710. }
  711. //-----------------------------------------------------------------------------
  712. // Purpose: Find a concept to question a nearby friend
  713. //-----------------------------------------------------------------------------
  714. bool CAI_PlayerAlly::SelectQuestionFriend( CBaseEntity *pFriend, AISpeechSelection_t *pSelection )
  715. {
  716. if ( !ShouldSpeakRandom( TLK_QUESTION, 3 ) )
  717. return false;
  718. // Tell the response rules who we're trying to question
  719. m_hPotentialSpeechTarget = pFriend;
  720. m_iQARandomNumber = RandomInt(0,100);
  721. // If we haven't said hello, say hello first.
  722. // Only ever say hello to NPCs other than my type.
  723. if ( !GetExpresser()->SpokeConcept( TLK_HELLO_NPC ) && !FClassnameIs( this, pFriend->GetClassname()) )
  724. {
  725. if ( SelectSpeechResponse( TLK_HELLO_NPC, NULL, pFriend, pSelection ) )
  726. return true;
  727. GetExpresser()->SetSpokeConcept( TLK_HELLO_NPC, NULL );
  728. }
  729. return SelectSpeechResponse( TLK_QUESTION, NULL, pFriend, pSelection );
  730. }
  731. //-----------------------------------------------------------------------------
  732. // Purpose: Find a concept to answer our friend's question
  733. //-----------------------------------------------------------------------------
  734. bool CAI_PlayerAlly::SelectAnswerFriend( CBaseEntity *pFriend, AISpeechSelection_t *pSelection, bool bRespondingToHello )
  735. {
  736. // Tell the response rules who we're trying to answer
  737. m_hPotentialSpeechTarget = pFriend;
  738. if ( bRespondingToHello )
  739. {
  740. if ( SelectSpeechResponse( TLK_ANSWER_HELLO, NULL, pFriend, pSelection ) )
  741. return true;
  742. }
  743. return SelectSpeechResponse( TLK_ANSWER, NULL, pFriend, pSelection );
  744. }
  745. //-----------------------------------------------------------------------------
  746. // Purpose:
  747. //-----------------------------------------------------------------------------
  748. void CAI_PlayerAlly::InputAnswerQuestion( inputdata_t &inputdata )
  749. {
  750. AnswerQuestion( dynamic_cast<CAI_PlayerAlly *>(inputdata.pActivator), inputdata.value.Int(), false );
  751. }
  752. //-----------------------------------------------------------------------------
  753. // Purpose:
  754. //-----------------------------------------------------------------------------
  755. void CAI_PlayerAlly::InputAnswerQuestionHello( inputdata_t &inputdata )
  756. {
  757. AnswerQuestion( dynamic_cast<CAI_PlayerAlly *>(inputdata.pActivator), inputdata.value.Int(), true );
  758. }
  759. //-----------------------------------------------------------------------------
  760. // Purpose:
  761. //-----------------------------------------------------------------------------
  762. void CAI_PlayerAlly::AnswerQuestion( CAI_PlayerAlly *pQuestioner, int iQARandomNum, bool bAnsweringHello )
  763. {
  764. // Original questioner may have died
  765. if ( !pQuestioner )
  766. return;
  767. AISpeechSelection_t selection;
  768. // Use the random number that the questioner used to determine his Question (so we can match answers via response rules)
  769. m_iQARandomNumber = iQARandomNum;
  770. // The activator is the person we're responding to
  771. if ( SelectAnswerFriend( pQuestioner, &selection, bAnsweringHello ) )
  772. {
  773. if ( rr_debug_qa.GetBool() )
  774. {
  775. if ( bAnsweringHello )
  776. {
  777. Warning("Q&A: '%s' answered the Hello from '%s'\n", GetDebugName(), pQuestioner->GetDebugName() );
  778. }
  779. else
  780. {
  781. Warning("Q&A: '%s' answered the Question from '%s'\n", GetDebugName(), pQuestioner->GetDebugName() );
  782. }
  783. }
  784. Assert( !selection.response.IsEmpty() );
  785. SetSpeechTarget( selection.hSpeechTarget );
  786. SpeakDispatchResponse( selection.concept.c_str(), &selection.response, NULL );
  787. // Prevent idle speech for a while
  788. DeferAllIdleSpeech( random->RandomFloat( TALKER_DEFER_IDLE_SPEAK_MIN, TALKER_DEFER_IDLE_SPEAK_MAX ), GetSpeechTarget()->MyNPCPointer() );
  789. }
  790. else if ( rr_debug_qa.GetBool() )
  791. {
  792. Warning("Q&A: '%s' couldn't answer '%s'\n", GetDebugName(), pQuestioner->GetDebugName() );
  793. }
  794. }
  795. //-----------------------------------------------------------------------------
  796. // Output : int
  797. //-----------------------------------------------------------------------------
  798. int CAI_PlayerAlly::SelectNonCombatSpeech( AISpeechSelection_t *pSelection )
  799. {
  800. bool bResult = false;
  801. #ifdef HL2_EPISODIC
  802. // See if we can Q&A first
  803. if ( GetState() == NPC_STATE_IDLE || GetState() == NPC_STATE_ALERT )
  804. {
  805. bResult = SelectQuestionAndAnswerSpeech( pSelection );
  806. }
  807. #endif // HL2_EPISODIC
  808. if ( !bResult )
  809. {
  810. if ( GetState() == NPC_STATE_ALERT )
  811. {
  812. bResult = SelectAlertSpeech( pSelection );
  813. }
  814. else
  815. {
  816. bResult = SelectIdleSpeech( pSelection );
  817. }
  818. }
  819. return bResult;
  820. }
  821. //-----------------------------------------------------------------------------
  822. //-----------------------------------------------------------------------------
  823. int CAI_PlayerAlly::SelectNonCombatSpeechSchedule()
  824. {
  825. if ( !HasPendingSpeech() )
  826. {
  827. AISpeechSelection_t selection;
  828. if ( SelectNonCombatSpeech( &selection ) )
  829. {
  830. Assert( !selection.response.IsEmpty() );
  831. SetSpeechTarget( selection.hSpeechTarget );
  832. SetPendingSpeech( selection.concept.c_str(), &selection.response );
  833. }
  834. }
  835. if ( HasPendingSpeech() )
  836. {
  837. if ( m_TimePendingSet == gpGlobals->curtime || IsAllowedToSpeak( m_PendingConcept.c_str() ) )
  838. return SCHED_TALKER_SPEAK_PENDING_IDLE;
  839. }
  840. return SCHED_NONE;
  841. }
  842. //-----------------------------------------------------------------------------
  843. //-----------------------------------------------------------------------------
  844. int CAI_PlayerAlly::TranslateSchedule( int schedule )
  845. {
  846. if ( ( GetState() == NPC_STATE_IDLE || GetState() == NPC_STATE_ALERT ) &&
  847. ConditionInterruptsSchedule( schedule, COND_IDLE_INTERRUPT ) &&
  848. !HasCondition(COND_RECEIVED_ORDERS) )
  849. {
  850. int speechSchedule = SelectNonCombatSpeechSchedule();
  851. if ( speechSchedule != SCHED_NONE )
  852. return speechSchedule;
  853. }
  854. switch( schedule )
  855. {
  856. case SCHED_CHASE_ENEMY_FAILED:
  857. {
  858. int baseType = BaseClass::TranslateSchedule(schedule);
  859. if ( baseType != SCHED_CHASE_ENEMY_FAILED )
  860. return baseType;
  861. return SCHED_TAKE_COVER_FROM_ENEMY;
  862. }
  863. break;
  864. }
  865. return BaseClass::TranslateSchedule( schedule );
  866. }
  867. //-----------------------------------------------------------------------------
  868. //-----------------------------------------------------------------------------
  869. void CAI_PlayerAlly::OnStartSchedule( int schedule )
  870. {
  871. if ( schedule == SCHED_HIDE_AND_RELOAD )
  872. SpeakIfAllowed( TLK_HIDEANDRELOAD );
  873. BaseClass::OnStartSchedule( schedule );
  874. }
  875. //-----------------------------------------------------------------------------
  876. //-----------------------------------------------------------------------------
  877. void CAI_PlayerAlly::StartTask( const Task_t *pTask )
  878. {
  879. switch ( pTask->iTask )
  880. {
  881. case TASK_MOVE_AWAY_PATH:
  882. {
  883. if ( HasCondition( COND_PLAYER_PUSHING ) && AI_IsSinglePlayer() )
  884. {
  885. // @TODO (toml 10-22-04): cope with multiplayer push
  886. GetMotor()->SetIdealYawToTarget( UTIL_GetLocalPlayer()->WorldSpaceCenter() );
  887. }
  888. BaseClass::StartTask( pTask );
  889. break;
  890. }
  891. case TASK_PLAY_SCRIPT:
  892. SetSpeechTarget( NULL );
  893. BaseClass::StartTask( pTask );
  894. break;
  895. case TASK_TALKER_SPEAK_PENDING:
  896. if ( !m_PendingConcept.empty() )
  897. {
  898. AI_Response response(m_PendingResponse);
  899. SpeakDispatchResponse( m_PendingConcept.c_str(), &response, NULL );
  900. m_PendingConcept.erase();
  901. TaskComplete();
  902. }
  903. else
  904. TaskFail( FAIL_NO_SOUND );
  905. break;
  906. default:
  907. BaseClass::StartTask( pTask );
  908. }
  909. }
  910. //-----------------------------------------------------------------------------
  911. //-----------------------------------------------------------------------------
  912. void CAI_PlayerAlly::RunTask( const Task_t *pTask )
  913. {
  914. BaseClass::RunTask( pTask );
  915. }
  916. //-----------------------------------------------------------------------------
  917. //-----------------------------------------------------------------------------
  918. void CAI_PlayerAlly::TaskFail( AI_TaskFailureCode_t code )
  919. {
  920. if ( IsCurSchedule( SCHED_TALKER_SPEAK_PENDING_IDLE, false ) )
  921. ClearPendingSpeech();
  922. BaseClass::TaskFail( code );
  923. }
  924. //-----------------------------------------------------------------------------
  925. //-----------------------------------------------------------------------------
  926. void CAI_PlayerAlly::ClearTransientConditions()
  927. {
  928. CAI_BaseNPC::ClearTransientConditions();
  929. }
  930. //-----------------------------------------------------------------------------
  931. //-----------------------------------------------------------------------------
  932. void CAI_PlayerAlly::Touch( CBaseEntity *pOther )
  933. {
  934. BaseClass::Touch( pOther );
  935. // Did the player touch me?
  936. if ( pOther->IsPlayer() )
  937. {
  938. // Ignore if pissed at player
  939. if ( m_afMemory & bits_MEMORY_PROVOKED )
  940. return;
  941. // Stay put during speech
  942. if ( GetExpresser()->IsSpeaking() )
  943. return;
  944. TestPlayerPushing( pOther );
  945. }
  946. }
  947. //-----------------------------------------------------------------------------
  948. //-----------------------------------------------------------------------------
  949. void CAI_PlayerAlly::OnKilledNPC( CBaseCombatCharacter *pKilled )
  950. {
  951. if ( pKilled )
  952. {
  953. if ( !pKilled->IsNPC() ||
  954. ( pKilled->MyNPCPointer()->GetLastPlayerDamageTime() == 0 ||
  955. gpGlobals->curtime - pKilled->MyNPCPointer()->GetLastPlayerDamageTime() > 5 ) )
  956. {
  957. SpeakIfAllowed( TLK_ENEMY_DEAD );
  958. }
  959. }
  960. }
  961. //-----------------------------------------------------------------------------
  962. void CAI_PlayerAlly::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr )
  963. {
  964. const char *pszHitLocCriterion = NULL;
  965. if ( ptr->hitgroup == HITGROUP_LEFTLEG || ptr->hitgroup == HITGROUP_RIGHTLEG )
  966. {
  967. pszHitLocCriterion = "shotloc:leg";
  968. }
  969. else if ( ptr->hitgroup == HITGROUP_LEFTARM || ptr->hitgroup == HITGROUP_RIGHTARM )
  970. {
  971. pszHitLocCriterion = "shotloc:arm";
  972. }
  973. else if ( ptr->hitgroup == HITGROUP_STOMACH )
  974. {
  975. pszHitLocCriterion = "shotloc:gut";
  976. }
  977. // set up the speech modifiers
  978. CFmtStrN<128> modifiers( "%s,damageammo:%s", pszHitLocCriterion, info.GetAmmoName() );
  979. SpeakIfAllowed( TLK_SHOT, modifiers );
  980. BaseClass::TraceAttack( info, vecDir, ptr );
  981. }
  982. //-----------------------------------------------------------------------------
  983. //-----------------------------------------------------------------------------
  984. int CAI_PlayerAlly::OnTakeDamage_Alive( const CTakeDamageInfo &info )
  985. {
  986. CTakeDamageInfo subInfo = info;
  987. // Vital allies never take more than 25% of their health in a single hit (except for physics damage)
  988. #ifdef HL2_DLL
  989. // Don't do damage reduction for DMG_GENERIC. This allows SetHealth inputs to still do full damage.
  990. if ( subInfo.GetDamageType() != DMG_GENERIC )
  991. {
  992. if ( Classify() == CLASS_PLAYER_ALLY_VITAL && !(subInfo.GetDamageType() & DMG_CRUSH) )
  993. {
  994. float flDamage = subInfo.GetDamage();
  995. if ( flDamage > ( GetMaxHealth() * 0.25 ) )
  996. {
  997. flDamage = ( GetMaxHealth() * 0.25 );
  998. subInfo.SetDamage( flDamage );
  999. }
  1000. }
  1001. }
  1002. #endif
  1003. return BaseClass::OnTakeDamage_Alive( subInfo );
  1004. }
  1005. //-----------------------------------------------------------------------------
  1006. //-----------------------------------------------------------------------------
  1007. int CAI_PlayerAlly::TakeHealth( float flHealth, int bitsDamageType )
  1008. {
  1009. int intPortion;
  1010. float floatPortion;
  1011. intPortion = ((int)flHealth);
  1012. floatPortion = flHealth - intPortion;
  1013. m_flHealthAccumulator += floatPortion;
  1014. while ( m_flHealthAccumulator > 1.0f )
  1015. {
  1016. m_flHealthAccumulator -= 1.0f;
  1017. intPortion += 1;
  1018. }
  1019. return BaseClass::TakeHealth( ((float)intPortion), bitsDamageType );
  1020. }
  1021. //-----------------------------------------------------------------------------
  1022. //-----------------------------------------------------------------------------
  1023. void CAI_PlayerAlly::Event_Killed( const CTakeDamageInfo &info )
  1024. {
  1025. // notify the player
  1026. if ( IsInPlayerSquad() )
  1027. {
  1028. CBasePlayer *player = AI_GetSinglePlayer();
  1029. if ( player )
  1030. {
  1031. variant_t emptyVariant;
  1032. player->AcceptInput( "OnSquadMemberKilled", this, this, emptyVariant, 0 );
  1033. }
  1034. }
  1035. if ( GetSpeechSemaphore( this )->GetOwner() == this )
  1036. GetSpeechSemaphore( this )->Release();
  1037. CAI_PlayerAlly *pMourner = dynamic_cast<CAI_PlayerAlly *>(FindSpeechTarget( AIST_NPCS ));
  1038. if ( pMourner )
  1039. {
  1040. pMourner->SpeakIfAllowed( TLK_ALLY_KILLED );
  1041. }
  1042. SetTarget( NULL );
  1043. // Don't finish that sentence
  1044. SentenceStop();
  1045. SetUse( NULL );
  1046. BaseClass::Event_Killed( info );
  1047. DisplayDeathMessage();
  1048. }
  1049. // Player allies should use simple shadows to save CPU. This means they can't
  1050. // be killed by crush damage.
  1051. bool CAI_PlayerAlly::CreateVPhysics()
  1052. {
  1053. bool bRet = BaseClass::CreateVPhysics();
  1054. return bRet;
  1055. }
  1056. //-----------------------------------------------------------------------------
  1057. void CAI_PlayerAlly::PainSound( const CTakeDamageInfo &info )
  1058. {
  1059. SpeakIfAllowed( TLK_WOUND );
  1060. }
  1061. //-----------------------------------------------------------------------------
  1062. // Purpose: Implemented to look at talk target
  1063. //-----------------------------------------------------------------------------
  1064. CBaseEntity *CAI_PlayerAlly::EyeLookTarget( void )
  1065. {
  1066. // FIXME: this should be in the VCD
  1067. // FIXME: this is dead code
  1068. if (GetExpresser()->IsSpeaking() && GetSpeechTarget() != NULL)
  1069. {
  1070. return GetSpeechTarget();
  1071. }
  1072. return NULL;
  1073. }
  1074. //-----------------------------------------------------------------------------
  1075. // Purpose: returns who we're talking to for vcd's
  1076. //-----------------------------------------------------------------------------
  1077. CBaseEntity *CAI_PlayerAlly::FindNamedEntity( const char *pszName, IEntityFindFilter *pFilter )
  1078. {
  1079. if ( !stricmp( pszName, "!speechtarget" ))
  1080. {
  1081. return GetSpeechTarget();
  1082. }
  1083. if ( !stricmp( pszName, "!friend" ))
  1084. {
  1085. return FindSpeechTarget( AIST_NPCS );
  1086. }
  1087. return BaseClass::FindNamedEntity( pszName, pFilter );
  1088. }
  1089. //-----------------------------------------------------------------------------
  1090. //-----------------------------------------------------------------------------
  1091. bool CAI_PlayerAlly::IsValidSpeechTarget( int flags, CBaseEntity *pEntity )
  1092. {
  1093. if ( pEntity == this )
  1094. return false;
  1095. if ( !(flags & AIST_IGNORE_RELATIONSHIP) )
  1096. {
  1097. if ( pEntity->IsPlayer() )
  1098. {
  1099. if ( !IsPlayerAlly( (CBasePlayer *)pEntity ) )
  1100. return false;
  1101. }
  1102. else
  1103. {
  1104. if ( IRelationType( pEntity ) != D_LI )
  1105. return false;
  1106. }
  1107. }
  1108. if ( !pEntity->IsAlive() )
  1109. // don't dead people
  1110. return false;
  1111. // Ignore no-target entities
  1112. if ( pEntity->GetFlags() & FL_NOTARGET )
  1113. return false;
  1114. CAI_BaseNPC *pNPC = pEntity->MyNPCPointer();
  1115. if ( pNPC )
  1116. {
  1117. // If not a NPC for some reason, or in a script.
  1118. if ( (pNPC->m_NPCState == NPC_STATE_SCRIPT || pNPC->m_NPCState == NPC_STATE_PRONE))
  1119. return false;
  1120. if ( pNPC->IsInAScript() )
  1121. return false;
  1122. // Don't bother people who don't want to be bothered
  1123. if ( !pNPC->CanBeUsedAsAFriend() )
  1124. return false;
  1125. }
  1126. if ( flags & AIST_FACING_TARGET )
  1127. {
  1128. if ( pEntity->IsPlayer() )
  1129. return HasCondition( COND_SEE_PLAYER );
  1130. else if ( !FInViewCone( pEntity ) )
  1131. return false;
  1132. }
  1133. return FVisible( pEntity );
  1134. }
  1135. //-----------------------------------------------------------------------------
  1136. //-----------------------------------------------------------------------------
  1137. CBaseEntity *CAI_PlayerAlly::FindSpeechTarget( int flags )
  1138. {
  1139. const Vector & vAbsOrigin = GetAbsOrigin();
  1140. float closestDistSq = FLT_MAX;
  1141. CBaseEntity * pNearest = NULL;
  1142. float distSq;
  1143. int i;
  1144. if ( flags & AIST_PLAYERS )
  1145. {
  1146. for ( i = 1; i <= gpGlobals->maxClients; i++ )
  1147. {
  1148. CBaseEntity *pPlayer = UTIL_PlayerByIndex( i );
  1149. if ( pPlayer )
  1150. {
  1151. distSq = ( vAbsOrigin - pPlayer->GetAbsOrigin() ).LengthSqr();
  1152. if ( distSq > Square(TALKRANGE_MIN) )
  1153. continue;
  1154. if ( !(flags & AIST_ANY_QUALIFIED) && distSq > closestDistSq )
  1155. continue;
  1156. if ( IsValidSpeechTarget( flags, pPlayer ) )
  1157. {
  1158. if ( flags & AIST_ANY_QUALIFIED )
  1159. return pPlayer;
  1160. closestDistSq = distSq;
  1161. pNearest = pPlayer;
  1162. }
  1163. }
  1164. }
  1165. }
  1166. if ( flags & AIST_NPCS )
  1167. {
  1168. for ( i = 0; i < g_AI_Manager.NumAIs(); i++ )
  1169. {
  1170. CAI_BaseNPC *pNPC = (g_AI_Manager.AccessAIs())[i];
  1171. distSq = ( vAbsOrigin - pNPC->GetAbsOrigin() ).LengthSqr();
  1172. if ( distSq > Square(TALKRANGE_MIN) )
  1173. continue;
  1174. if ( !(flags & AIST_ANY_QUALIFIED) && distSq > closestDistSq )
  1175. continue;
  1176. if ( IsValidSpeechTarget( flags, pNPC ) )
  1177. {
  1178. if ( flags & AIST_ANY_QUALIFIED )
  1179. return pNPC;
  1180. closestDistSq = distSq;
  1181. pNearest = pNPC;
  1182. }
  1183. }
  1184. }
  1185. return pNearest;
  1186. }
  1187. //-----------------------------------------------------------------------------
  1188. //-----------------------------------------------------------------------------
  1189. bool CAI_PlayerAlly::CanPlaySentence( bool fDisregardState )
  1190. {
  1191. if ( fDisregardState )
  1192. return BaseClass::CanPlaySentence( fDisregardState );
  1193. return IsOkToSpeak();
  1194. }
  1195. //-----------------------------------------------------------------------------
  1196. //-----------------------------------------------------------------------------
  1197. int CAI_PlayerAlly::PlayScriptedSentence( const char *pszSentence, float delay, float volume, soundlevel_t soundlevel, bool bConcurrent, CBaseEntity *pListener )
  1198. {
  1199. ClearCondition( COND_PLAYER_PUSHING ); // Forget about moving! I've got something to say!
  1200. int sentenceIndex = BaseClass::PlayScriptedSentence( pszSentence, delay, volume, soundlevel, bConcurrent, pListener );
  1201. SetSpeechTarget( pListener );
  1202. return sentenceIndex;
  1203. }
  1204. //------------------------------------------------------------------------------
  1205. //------------------------------------------------------------------------------
  1206. void CAI_PlayerAlly::DeferAllIdleSpeech( float flDelay, CAI_BaseNPC *pIgnore )
  1207. {
  1208. CAI_AllySpeechManager *pSpeechManager = GetAllySpeechManager();
  1209. if ( flDelay == -1 )
  1210. {
  1211. ConceptCategoryInfo_t *pCategoryInfo = pSpeechManager->GetConceptCategoryInfo( SPEECH_IDLE );
  1212. pSpeechManager->SetCategoryDelay( SPEECH_IDLE, pCategoryInfo->minGlobalDelay, pCategoryInfo->maxGlobalDelay );
  1213. }
  1214. else
  1215. pSpeechManager->SetCategoryDelay( SPEECH_IDLE, flDelay );
  1216. }
  1217. //------------------------------------------------------------------------------
  1218. //------------------------------------------------------------------------------
  1219. bool CAI_PlayerAlly::IsOkToSpeak( ConceptCategory_t category, bool fRespondingToPlayer )
  1220. {
  1221. CAI_AllySpeechManager *pSpeechManager = GetAllySpeechManager();
  1222. // if not alive, certainly don't speak
  1223. if ( !IsAlive() )
  1224. return false;
  1225. if ( m_spawnflags & SF_NPC_GAG )
  1226. return false;
  1227. // Don't speak if playing a script.
  1228. if ( ( m_NPCState == NPC_STATE_SCRIPT ) && !m_bCanSpeakWhileScripting )
  1229. return false;
  1230. // Don't speak if being eaten by a barnacle
  1231. if ( IsEFlagSet( EFL_IS_BEING_LIFTED_BY_BARNACLE ) )
  1232. return false;
  1233. if ( IsInAScript() && !m_bCanSpeakWhileScripting )
  1234. return false;
  1235. if ( !fRespondingToPlayer )
  1236. {
  1237. if ( !pSpeechManager->CategoryDelayExpired( category ) || !CategoryDelayExpired( category ) )
  1238. return false;
  1239. }
  1240. if ( category == SPEECH_IDLE )
  1241. {
  1242. if ( GetState() != NPC_STATE_IDLE && GetState() != NPC_STATE_ALERT )
  1243. return false;
  1244. if ( GetSpeechFilter() && GetSpeechFilter()->GetIdleModifier() < 0.001 )
  1245. return false;
  1246. }
  1247. // if player is not in pvs, don't speak
  1248. if ( !UTIL_FindClientInPVS(edict()) )
  1249. return false;
  1250. if ( category != SPEECH_PRIORITY )
  1251. {
  1252. // if someone else is talking, don't speak
  1253. if ( !GetExpresser()->SemaphoreIsAvailable( this ) )
  1254. return false;
  1255. if ( fRespondingToPlayer )
  1256. {
  1257. if ( !GetExpresser()->CanSpeakAfterMyself() )
  1258. return false;
  1259. }
  1260. else
  1261. {
  1262. if ( !GetExpresser()->CanSpeak() )
  1263. return false;
  1264. }
  1265. // Don't talk if we're too far from the player
  1266. CBaseEntity *pPlayer = AI_GetSinglePlayer();
  1267. if ( pPlayer )
  1268. {
  1269. float flDist = sv_npc_talker_maxdist.GetFloat();
  1270. flDist *= flDist;
  1271. if ( (pPlayer->WorldSpaceCenter() - WorldSpaceCenter()).LengthSqr() > flDist )
  1272. return false;
  1273. }
  1274. }
  1275. if ( fRespondingToPlayer )
  1276. {
  1277. // If we're responding to the player, don't respond if the scene has speech in it
  1278. if ( IsRunningScriptedSceneWithSpeechAndNotPaused( this ) )
  1279. {
  1280. if( rr_debugresponses.GetInt() > 0 )
  1281. {
  1282. DevMsg("%s not allowed to speak because they are in a scripted scene\n", GetDebugName() );
  1283. }
  1284. return false;
  1285. }
  1286. }
  1287. else
  1288. {
  1289. // If we're not responding to the player, don't talk if running a logic_choreo
  1290. if ( IsRunningScriptedSceneAndNotPaused( this ) )
  1291. {
  1292. if( rr_debugresponses.GetInt() > 0 )
  1293. {
  1294. DevMsg("%s not allowed to speak because they are in a scripted scene\n", GetDebugName() );
  1295. }
  1296. return false;
  1297. }
  1298. }
  1299. return true;
  1300. }
  1301. //------------------------------------------------------------------------------
  1302. //------------------------------------------------------------------------------
  1303. bool CAI_PlayerAlly::IsOkToSpeak( void )
  1304. {
  1305. return IsOkToSpeak( SPEECH_IDLE );
  1306. }
  1307. //------------------------------------------------------------------------------
  1308. //------------------------------------------------------------------------------
  1309. bool CAI_PlayerAlly::IsOkToCombatSpeak( void )
  1310. {
  1311. return IsOkToSpeak( SPEECH_IMPORTANT );
  1312. }
  1313. //------------------------------------------------------------------------------
  1314. //------------------------------------------------------------------------------
  1315. bool CAI_PlayerAlly::IsOkToSpeakInResponseToPlayer( void )
  1316. {
  1317. return IsOkToSpeak( SPEECH_IMPORTANT, true );
  1318. }
  1319. //-----------------------------------------------------------------------------
  1320. // Purpose: Return true if I should speak based on the chance & the speech filter's modifier
  1321. //-----------------------------------------------------------------------------
  1322. bool CAI_PlayerAlly::ShouldSpeakRandom( AIConcept_t concept, int iChance )
  1323. {
  1324. CAI_AllySpeechManager * pSpeechManager = GetAllySpeechManager();
  1325. ConceptInfo_t * pInfo = pSpeechManager->GetConceptInfo( concept );
  1326. ConceptCategory_t category = ( pInfo ) ? pInfo->category : SPEECH_IDLE;
  1327. if ( GetSpeechFilter() )
  1328. {
  1329. if ( category == SPEECH_IDLE )
  1330. {
  1331. float flModifier = GetSpeechFilter()->GetIdleModifier();
  1332. if ( flModifier < 0.001 )
  1333. return false;
  1334. iChance = floor( (float)iChance / flModifier );
  1335. }
  1336. }
  1337. if ( iChance < 1 )
  1338. return false;
  1339. if ( iChance == 1 )
  1340. return true;
  1341. return (random->RandomInt(1,iChance) == 1);
  1342. }
  1343. //-----------------------------------------------------------------------------
  1344. //-----------------------------------------------------------------------------
  1345. bool CAI_PlayerAlly::IsAllowedToSpeak( AIConcept_t concept, bool bRespondingToPlayer )
  1346. {
  1347. CAI_AllySpeechManager * pSpeechManager = GetAllySpeechManager();
  1348. ConceptInfo_t * pInfo = pSpeechManager->GetConceptInfo( concept );
  1349. ConceptCategory_t category = ( pInfo ) ? pInfo->category : SPEECH_IDLE;
  1350. if ( !IsOkToSpeak( category, bRespondingToPlayer ) )
  1351. return false;
  1352. if ( GetSpeechFilter() && GetSpeechFilter()->NeverSayHello() )
  1353. {
  1354. if ( CompareConcepts( concept, TLK_HELLO ) )
  1355. return false;
  1356. if ( CompareConcepts( concept, TLK_HELLO_NPC ) )
  1357. return false;
  1358. }
  1359. if ( !pSpeechManager->ConceptDelayExpired( concept ) )
  1360. return false;
  1361. if ( ( pInfo && pInfo->flags & AICF_SPEAK_ONCE ) && GetExpresser()->SpokeConcept( concept ) )
  1362. return false;
  1363. if ( !GetExpresser()->CanSpeakConcept( concept ) )
  1364. return false;
  1365. return true;
  1366. }
  1367. //-----------------------------------------------------------------------------
  1368. //-----------------------------------------------------------------------------
  1369. bool CAI_PlayerAlly::SpeakIfAllowed( AIConcept_t concept, const char *modifiers, bool bRespondingToPlayer, char *pszOutResponseChosen, size_t bufsize )
  1370. {
  1371. if ( IsAllowedToSpeak( concept, bRespondingToPlayer ) )
  1372. {
  1373. return Speak( concept, modifiers, pszOutResponseChosen, bufsize );
  1374. }
  1375. return false;
  1376. }
  1377. //-----------------------------------------------------------------------------
  1378. //-----------------------------------------------------------------------------
  1379. void CAI_PlayerAlly::ModifyOrAppendCriteria( AI_CriteriaSet& set )
  1380. {
  1381. BaseClass::ModifyOrAppendCriteria( set );
  1382. if ( m_hPotentialSpeechTarget )
  1383. {
  1384. set.AppendCriteria( "speechtarget", m_hPotentialSpeechTarget->GetClassname() );
  1385. set.AppendCriteria( "speechtargetname", STRING(m_hPotentialSpeechTarget->GetEntityName()) );
  1386. set.AppendCriteria( "randomnum", UTIL_VarArgs("%d", m_iQARandomNumber) );
  1387. }
  1388. // Do we have a speech filter? If so, append it's criteria too
  1389. if ( GetSpeechFilter() )
  1390. {
  1391. GetSpeechFilter()->AppendContextToCriteria( set );
  1392. }
  1393. }
  1394. //-----------------------------------------------------------------------------
  1395. //-----------------------------------------------------------------------------
  1396. void CAI_PlayerAlly::OnSpokeConcept( AIConcept_t concept, AI_Response *response )
  1397. {
  1398. CAI_AllySpeechManager *pSpeechManager = GetAllySpeechManager();
  1399. pSpeechManager->OnSpokeConcept( this, concept, response );
  1400. if( response != NULL && (response->GetParams()->flags & AI_ResponseParams::RG_WEAPONDELAY) )
  1401. {
  1402. // Stop shooting, as instructed, so that my speech can be heard.
  1403. GetShotRegulator()->FireNoEarlierThan( gpGlobals->curtime + response->GetWeaponDelay() );
  1404. }
  1405. }
  1406. //-----------------------------------------------------------------------------
  1407. //-----------------------------------------------------------------------------
  1408. void CAI_PlayerAlly::OnStartSpeaking()
  1409. {
  1410. // If you say anything, don't greet the player - you may have already spoken to them
  1411. if ( !GetExpresser()->SpokeConcept( TLK_HELLO ) )
  1412. {
  1413. GetExpresser()->SetSpokeConcept( TLK_HELLO, NULL );
  1414. }
  1415. }
  1416. //-----------------------------------------------------------------------------
  1417. // Purpose: Mapmaker input to force this NPC to speak a response rules concept
  1418. //-----------------------------------------------------------------------------
  1419. void CAI_PlayerAlly::InputSpeakResponseConcept( inputdata_t &inputdata )
  1420. {
  1421. SpeakMapmakerInterruptConcept( inputdata.value.StringID() );
  1422. }
  1423. //-----------------------------------------------------------------------------
  1424. // Allows mapmakers to override NPC_STATE_SCRIPT or IsScripting() for responses.
  1425. //-----------------------------------------------------------------------------
  1426. void CAI_PlayerAlly::InputEnableSpeakWhileScripting( inputdata_t &inputdata )
  1427. {
  1428. m_bCanSpeakWhileScripting = true;
  1429. }
  1430. void CAI_PlayerAlly::InputDisableSpeakWhileScripting( inputdata_t &inputdata )
  1431. {
  1432. m_bCanSpeakWhileScripting = false;
  1433. }
  1434. //-----------------------------------------------------------------------------
  1435. // Purpose:
  1436. //-----------------------------------------------------------------------------
  1437. bool CAI_PlayerAlly::SpeakMapmakerInterruptConcept( string_t iszConcept )
  1438. {
  1439. if (!IsOkToSpeakInResponseToPlayer())
  1440. return false;
  1441. Speak( STRING(iszConcept) );
  1442. return true;
  1443. }
  1444. //-----------------------------------------------------------------------------
  1445. // Purpose:
  1446. //-----------------------------------------------------------------------------
  1447. bool CAI_PlayerAlly::CanRespondToEvent( const char *ResponseConcept )
  1448. {
  1449. return true;
  1450. }
  1451. //-----------------------------------------------------------------------------
  1452. // Purpose:
  1453. //-----------------------------------------------------------------------------
  1454. bool CAI_PlayerAlly::RespondedTo( const char *ResponseConcept, bool bForce, bool bCancelScene )
  1455. {
  1456. if ( bForce )
  1457. {
  1458. // We're being forced to respond to the event, probably because it's the
  1459. // player dying or something equally important.
  1460. AI_Response result;
  1461. AIConcept_t tempConcept( ResponseConcept );
  1462. if ( FindResponse( result, tempConcept, NULL ) )
  1463. {
  1464. // We've got something to say. Stop any scenes we're in, and speak the response.
  1465. if ( bCancelScene )
  1466. RemoveActorFromScriptedScenes( this, false );
  1467. bool spoke = SpeakDispatchResponse( tempConcept, &result, NULL );
  1468. return spoke;
  1469. }
  1470. return false;
  1471. }
  1472. if ( SpeakIfAllowed( ResponseConcept, NULL, true ) )
  1473. return true;
  1474. return false;
  1475. }
  1476. //-----------------------------------------------------------------------------
  1477. //
  1478. // Schedules
  1479. //
  1480. //-----------------------------------------------------------------------------
  1481. AI_BEGIN_CUSTOM_NPC(talk_monster_base,CAI_PlayerAlly)
  1482. DECLARE_TASK(TASK_TALKER_SPEAK_PENDING)
  1483. DECLARE_CONDITION(COND_TALKER_CLIENTUNSEEN)
  1484. DECLARE_CONDITION(COND_TALKER_PLAYER_DEAD)
  1485. DECLARE_CONDITION(COND_TALKER_PLAYER_STARING)
  1486. //=========================================================
  1487. // > SCHED_TALKER_SPEAK_PENDING_IDLE
  1488. //=========================================================
  1489. DEFINE_SCHEDULE
  1490. (
  1491. SCHED_TALKER_SPEAK_PENDING_IDLE,
  1492. " Tasks"
  1493. " TASK_TALKER_SPEAK_PENDING 0"
  1494. " TASK_STOP_MOVING 0"
  1495. " TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE"
  1496. " TASK_WAIT_FOR_SPEAK_FINISH 0"
  1497. " TASK_WAIT_RANDOM 0.5"
  1498. ""
  1499. " Interrupts"
  1500. " COND_NEW_ENEMY"
  1501. " COND_LIGHT_DAMAGE"
  1502. " COND_HEAVY_DAMAGE"
  1503. " COND_HEAR_DANGER"
  1504. " COND_HEAR_COMBAT"
  1505. " COND_PLAYER_PUSHING"
  1506. " COND_GIVE_WAY"
  1507. )
  1508. //=========================================================
  1509. // > SCHED_TALKER_SPEAK_PENDING_ALERT
  1510. //=========================================================
  1511. DEFINE_SCHEDULE
  1512. (
  1513. SCHED_TALKER_SPEAK_PENDING_ALERT,
  1514. " Tasks"
  1515. " TASK_TALKER_SPEAK_PENDING 0"
  1516. " TASK_STOP_MOVING 0"
  1517. " TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE"
  1518. " TASK_WAIT_FOR_SPEAK_FINISH 0"
  1519. " TASK_WAIT_RANDOM 0.5"
  1520. ""
  1521. " Interrupts"
  1522. " COND_NEW_ENEMY"
  1523. " COND_LIGHT_DAMAGE"
  1524. " COND_HEAVY_DAMAGE"
  1525. " COND_HEAR_DANGER"
  1526. " COND_PLAYER_PUSHING"
  1527. " COND_GIVE_WAY"
  1528. )
  1529. //=========================================================
  1530. // > SCHED_TALKER_SPEAK_PENDING_COMBAT
  1531. //=========================================================
  1532. DEFINE_SCHEDULE
  1533. (
  1534. SCHED_TALKER_SPEAK_PENDING_COMBAT,
  1535. " Tasks"
  1536. " TASK_TALKER_SPEAK_PENDING 0"
  1537. " TASK_STOP_MOVING 0"
  1538. " TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE"
  1539. " TASK_WAIT_FOR_SPEAK_FINISH 0"
  1540. ""
  1541. " Interrupts"
  1542. " COND_HEAVY_DAMAGE"
  1543. " COND_HEAR_DANGER"
  1544. )
  1545. AI_END_CUSTOM_NPC()
  1546. //-----------------------------------------------------------------------------