Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

459 lines
12 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. //=========================================================
  9. // Generic NPC - purely for scripted sequence work.
  10. //=========================================================
  11. #include "cbase.h"
  12. #include "shareddefs.h"
  13. #include "npcevent.h"
  14. #include "ai_basenpc.h"
  15. #include "ai_hull.h"
  16. #include "ai_baseactor.h"
  17. #include "tier1/strtools.h"
  18. #include "vstdlib/random.h"
  19. #include "engine/IEngineSound.h"
  20. // memdbgon must be the last include file in a .cpp file!!!
  21. #include "tier0/memdbgon.h"
  22. ConVar flex_looktime( "flex_looktime", "5" );
  23. //---------------------------------------------------------
  24. // Sounds
  25. //---------------------------------------------------------
  26. //=========================================================
  27. // NPC's Anim Events Go Here
  28. //=========================================================
  29. class CGenericActor : public CAI_BaseActor
  30. {
  31. public:
  32. DECLARE_CLASS( CGenericActor, CAI_BaseActor );
  33. void Spawn( void );
  34. void Precache( void );
  35. float MaxYawSpeed( void );
  36. Class_T Classify ( void );
  37. void HandleAnimEvent( animevent_t *pEvent );
  38. int GetSoundInterests ( void );
  39. void TempGunEffect( void );
  40. string_t m_strHullName;
  41. DECLARE_DATADESC();
  42. };
  43. LINK_ENTITY_TO_CLASS( generic_actor, CGenericActor );
  44. BEGIN_DATADESC( CGenericActor )
  45. DEFINE_KEYFIELD(m_strHullName, FIELD_STRING, "hull_name" ),
  46. END_DATADESC()
  47. //=========================================================
  48. // Classify - indicates this NPC's place in the
  49. // relationship table.
  50. //=========================================================
  51. Class_T CGenericActor::Classify ( void )
  52. {
  53. return CLASS_NONE;
  54. }
  55. //=========================================================
  56. // MaxYawSpeed - allows each sequence to have a different
  57. // turn rate associated with it.
  58. //=========================================================
  59. float CGenericActor::MaxYawSpeed ( void )
  60. {
  61. return 90;
  62. }
  63. //=========================================================
  64. // HandleAnimEvent - catches the NPC-specific messages
  65. // that occur when tagged animation frames are played.
  66. //=========================================================
  67. void CGenericActor::HandleAnimEvent( animevent_t *pEvent )
  68. {
  69. BaseClass::HandleAnimEvent( pEvent );
  70. }
  71. //=========================================================
  72. // GetSoundInterests - generic NPC can't hear.
  73. //=========================================================
  74. int CGenericActor::GetSoundInterests ( void )
  75. {
  76. return NULL;
  77. }
  78. //=========================================================
  79. // Spawn
  80. //=========================================================
  81. void CGenericActor::Spawn()
  82. {
  83. Precache();
  84. SetModel( STRING( GetModelName() ) );
  85. /*
  86. if ( FStrEq( STRING( GetModelName() ), "models/player.mdl" ) )
  87. UTIL_SetSize(this, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX);
  88. else
  89. UTIL_SetSize(this, VEC_HULL_MIN, VEC_HULL_MAX);
  90. */
  91. if ( FStrEq( STRING( GetModelName() ), "models/player.mdl" ) ||
  92. FStrEq( STRING( GetModelName() ), "models/holo.mdl" ) ||
  93. FStrEq( STRING( GetModelName() ), "models/blackout.mdl" ) )
  94. {
  95. UTIL_SetSize(this, VEC_HULL_MIN, VEC_HULL_MAX);
  96. }
  97. else
  98. {
  99. UTIL_SetSize(this, NAI_Hull::Mins(HULL_HUMAN), NAI_Hull::Maxs(HULL_HUMAN));
  100. }
  101. if ( !FStrEq( STRING( GetModelName() ), "models/blackout.mdl" ) )
  102. {
  103. SetSolid( SOLID_BBOX );
  104. AddSolidFlags( FSOLID_NOT_STANDABLE );
  105. }
  106. else
  107. {
  108. SetSolid( SOLID_NONE );
  109. }
  110. SetMoveType( MOVETYPE_STEP );
  111. SetBloodColor( BLOOD_COLOR_RED );
  112. m_iHealth = 8;
  113. m_flFieldOfView = 0.5;// indicates the width of this NPC's forward view cone ( as a dotproduct result )
  114. m_NPCState = NPC_STATE_NONE;
  115. CapabilitiesAdd( bits_CAP_MOVE_GROUND | bits_CAP_OPEN_DOORS );
  116. // remove head turn if no eyes or forward attachment
  117. if (LookupAttachment( "eyes" ) > 0 && LookupAttachment( "forward" ) > 0)
  118. {
  119. CapabilitiesAdd( bits_CAP_TURN_HEAD | bits_CAP_ANIMATEDFACE );
  120. }
  121. if (m_strHullName != NULL_STRING)
  122. {
  123. SetHullType( NAI_Hull::LookupId( STRING( m_strHullName ) ) );
  124. }
  125. else
  126. {
  127. SetHullType( HULL_HUMAN );
  128. }
  129. SetHullSizeNormal( );
  130. NPCInit();
  131. }
  132. //=========================================================
  133. // Precache - precaches all resources this NPC needs
  134. //=========================================================
  135. void CGenericActor::Precache()
  136. {
  137. PrecacheModel( STRING( GetModelName() ) );
  138. }
  139. //=========================================================
  140. // AI Schedules Specific to this NPC
  141. //=========================================================
  142. // -----------------------------------------------------------------------
  143. // FIXME: delete this code
  144. class CFlextalkActor : public CGenericActor
  145. {
  146. private:
  147. DECLARE_CLASS( CFlextalkActor, CGenericActor );
  148. public:
  149. DECLARE_DATADESC();
  150. CFlextalkActor() { m_iszSentence = NULL_STRING; m_sentence = 0; }
  151. //void GenericCyclerSpawn(char *szModel, Vector vecMin, Vector vecMax);
  152. //virtual int ObjectCaps( void ) { return (BaseClass::ObjectCaps() | FCAP_IMPULSE_USE); }
  153. //int OnTakeDamage( CBaseEntity *pInflictor, CBaseEntity *pAttacker, float flDamage, int bitsDamageType );
  154. //void Spawn( void );
  155. //void Precache( void );
  156. //void Think( void );
  157. virtual void ProcessSceneEvents( void );
  158. // Don't treat as a live target
  159. //virtual bool IsAlive( void ) { return FALSE; }
  160. float m_flextime;
  161. LocalFlexController_t m_flexnum;
  162. float m_flextarget[64];
  163. float m_blinktime;
  164. float m_looktime;
  165. Vector m_lookTarget;
  166. float m_speaktime;
  167. int m_istalking;
  168. int m_phoneme;
  169. string_t m_iszSentence;
  170. int m_sentence;
  171. void SetFlexTarget( LocalFlexController_t flexnum, float value );
  172. LocalFlexController_t LookupFlex( const char *szTarget );
  173. };
  174. BEGIN_DATADESC( CFlextalkActor )
  175. DEFINE_FIELD( m_flextime, FIELD_TIME ),
  176. DEFINE_FIELD( m_flexnum, FIELD_INTEGER ),
  177. DEFINE_ARRAY( m_flextarget, FIELD_FLOAT, 64 ),
  178. DEFINE_FIELD( m_blinktime, FIELD_TIME ),
  179. DEFINE_FIELD( m_looktime, FIELD_TIME ),
  180. DEFINE_FIELD( m_lookTarget, FIELD_POSITION_VECTOR ),
  181. DEFINE_FIELD( m_speaktime, FIELD_TIME ),
  182. DEFINE_FIELD( m_istalking, FIELD_INTEGER ),
  183. DEFINE_FIELD( m_phoneme, FIELD_INTEGER ),
  184. DEFINE_KEYFIELD( m_iszSentence, FIELD_STRING, "Sentence" ),
  185. DEFINE_FIELD( m_sentence, FIELD_INTEGER ),
  186. END_DATADESC()
  187. LINK_ENTITY_TO_CLASS( cycler_actor, CFlextalkActor );
  188. extern ConVar flex_expression;
  189. extern ConVar flex_talk;
  190. // Cycler member functions
  191. extern const char *predef_flexcontroller_names[];
  192. extern float predef_flexcontroller_values[7][30];
  193. void CFlextalkActor::SetFlexTarget( LocalFlexController_t flexnum, float value )
  194. {
  195. m_flextarget[flexnum] = value;
  196. const char *pszType = GetFlexControllerType( flexnum );
  197. for (LocalFlexController_t i = LocalFlexController_t(0); i < GetNumFlexControllers(); i++)
  198. {
  199. if (i != flexnum)
  200. {
  201. const char *pszOtherType = GetFlexControllerType( i );
  202. if (stricmp( pszType, pszOtherType ) == 0)
  203. {
  204. m_flextarget[i] = 0;
  205. }
  206. }
  207. }
  208. float value2 = value;
  209. if (1 || random->RandomFloat( 0.0, 1.0 ) < 0.2)
  210. {
  211. value2 = random->RandomFloat( value - 0.2, value + 0.2 );
  212. value2 = clamp( value2, 0.0f, 1.0f );
  213. }
  214. // HACK, for now, consider then linked is named "right_" or "left_"
  215. if (strncmp( "right_", GetFlexControllerName( flexnum ), 6 ) == 0)
  216. {
  217. m_flextarget[flexnum+1] = value2;
  218. }
  219. else if (strncmp( "left_", GetFlexControllerName( flexnum ), 5 ) == 0)
  220. {
  221. m_flextarget[flexnum-1] = value2;
  222. }
  223. }
  224. LocalFlexController_t CFlextalkActor::LookupFlex( const char *szTarget )
  225. {
  226. for (LocalFlexController_t i = LocalFlexController_t(0); i < GetNumFlexControllers(); i++)
  227. {
  228. const char *pszFlex = GetFlexControllerName( i );
  229. if (stricmp( szTarget, pszFlex ) == 0)
  230. {
  231. return i;
  232. }
  233. }
  234. return LocalFlexController_t(-1);
  235. }
  236. void CFlextalkActor::ProcessSceneEvents( void )
  237. {
  238. if ( HasSceneEvents() )
  239. {
  240. BaseClass::ProcessSceneEvents( );
  241. return;
  242. }
  243. // only do this if they have more than eyelid movement
  244. if (GetNumFlexControllers() > 2)
  245. {
  246. const char *pszExpression = flex_expression.GetString();
  247. if (pszExpression && pszExpression[0] == '+' && pszExpression[1] != '\0')
  248. {
  249. int i;
  250. int j = atoi( &pszExpression[1] );
  251. for (i = 0; i < GetNumFlexControllers(); i++)
  252. {
  253. m_flextarget[m_flexnum] = 0;
  254. }
  255. for (i = 0; i < 35 && predef_flexcontroller_names[i]; i++)
  256. {
  257. m_flexnum = LookupFlex( predef_flexcontroller_names[i] );
  258. m_flextarget[m_flexnum] = predef_flexcontroller_values[j][i];
  259. // Msg( "%s %.3f\n", predef_flexcontroller_names[i], predef_flexcontroller_values[j][i] );
  260. }
  261. }
  262. else if (pszExpression && pszExpression[0] != '\0' && strcmp(pszExpression, "+") != 0)
  263. {
  264. char szExpression[128];
  265. char szTemp[32];
  266. Q_strncpy( szExpression, pszExpression ,sizeof(szExpression));
  267. char *pszExpression = szExpression;
  268. while (*pszExpression != '\0')
  269. {
  270. if (*pszExpression == '+')
  271. *pszExpression = ' ';
  272. pszExpression++;
  273. }
  274. pszExpression = szExpression;
  275. while (*pszExpression)
  276. {
  277. if (*pszExpression != ' ')
  278. {
  279. if (*pszExpression == '-')
  280. {
  281. for (LocalFlexController_t i = LocalFlexController_t(0); i < GetNumFlexControllers(); i++)
  282. {
  283. m_flextarget[i] = 0;
  284. }
  285. }
  286. else if (*pszExpression == '?')
  287. {
  288. for (LocalFlexController_t i = LocalFlexController_t(0); i < GetNumFlexControllers(); i++)
  289. {
  290. Msg( "\"%s\" ", GetFlexControllerName( i ) );
  291. }
  292. Msg( "\n" );
  293. flex_expression.SetValue( "" );
  294. }
  295. else
  296. {
  297. if (sscanf( pszExpression, "%31s", szTemp ) == 1)
  298. {
  299. m_flexnum = LookupFlex( szTemp );
  300. if (m_flexnum != LocalFlexController_t(-1) && m_flextarget[m_flexnum] != 1)
  301. {
  302. m_flextarget[m_flexnum] = 1.0;
  303. // SetFlexTarget( m_flexnum );
  304. }
  305. pszExpression += strlen( szTemp ) - 1;
  306. }
  307. }
  308. }
  309. pszExpression++;
  310. }
  311. }
  312. else if (m_flextime < gpGlobals->curtime)
  313. {
  314. m_flextime = gpGlobals->curtime + random->RandomFloat( 0.3, 0.5 ) * (30.0 / GetNumFlexControllers());
  315. m_flexnum = (LocalFlexController_t)random->RandomInt( 0, GetNumFlexControllers() - 1 );
  316. if (m_flextarget[m_flexnum] == 1)
  317. {
  318. m_flextarget[m_flexnum] = 0;
  319. }
  320. else if (stricmp( GetFlexControllerType( m_flexnum ), "phoneme" ) != 0)
  321. {
  322. if (strstr( GetFlexControllerName( m_flexnum ), "upper_raiser" ) == NULL)
  323. {
  324. Msg( "%s:%s\n", GetFlexControllerType( m_flexnum ), GetFlexControllerName( m_flexnum ) );
  325. SetFlexTarget( m_flexnum, random->RandomFloat( 0.5, 1.0 ) );
  326. }
  327. }
  328. }
  329. // slide it up.
  330. for (LocalFlexController_t i = LocalFlexController_t(0); i < GetNumFlexControllers(); i++)
  331. {
  332. float weight = GetFlexWeight( i );
  333. if (weight != m_flextarget[i])
  334. {
  335. weight = weight + (m_flextarget[i] - weight) / random->RandomFloat( 2.0, 4.0 );
  336. }
  337. weight = clamp( weight, 0.0f, 1.0f );
  338. SetFlexWeight( i, weight );
  339. }
  340. if (flex_talk.GetInt() == -1)
  341. {
  342. m_istalking = 1;
  343. char pszSentence[256];
  344. Q_snprintf( pszSentence,sizeof(pszSentence), "%s%d", STRING(m_iszSentence), m_sentence++ );
  345. int sentenceIndex = engine->SentenceIndexFromName( pszSentence );
  346. if (sentenceIndex >= 0)
  347. {
  348. Msg( "%d : %s\n", sentenceIndex, pszSentence );
  349. CPASAttenuationFilter filter( this );
  350. CBaseEntity::EmitSentenceByIndex( filter, entindex(), CHAN_VOICE, sentenceIndex, 1, SNDLVL_TALKING, 0, PITCH_NORM );
  351. }
  352. else
  353. {
  354. m_sentence = 0;
  355. }
  356. flex_talk.SetValue( "0" );
  357. }
  358. else if (flex_talk.GetInt() == -2)
  359. {
  360. m_flNextEyeLookTime = gpGlobals->curtime + 1000.0;
  361. }
  362. else if (flex_talk.GetInt() == -3)
  363. {
  364. m_flNextEyeLookTime = gpGlobals->curtime;
  365. flex_talk.SetValue( "0" );
  366. }
  367. else if (flex_talk.GetInt() == -4)
  368. {
  369. AddLookTarget( UTIL_PlayerByIndex( 1 ), 0.5, flex_looktime.GetFloat() );
  370. flex_talk.SetValue( "0" );
  371. }
  372. else if (flex_talk.GetInt() == -5)
  373. {
  374. PickLookTarget( true );
  375. flex_talk.SetValue( "0" );
  376. }
  377. }
  378. }