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.

1932 lines
57 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "sceneentity.h"
  8. #include "choreoevent.h"
  9. #include "choreoscene.h"
  10. #include "choreoactor.h"
  11. #include "ai_baseactor.h"
  12. #include "ai_navigator.h"
  13. #include "saverestore_utlvector.h"
  14. #include "bone_setup.h"
  15. #include "physics_npc_solver.h"
  16. // memdbgon must be the last include file in a .cpp file!!!
  17. #include "tier0/memdbgon.h"
  18. ConVar flex_minplayertime( "flex_minplayertime", "5" );
  19. ConVar flex_maxplayertime( "flex_maxplayertime", "7" );
  20. ConVar flex_minawaytime( "flex_minawaytime", "0.5" );
  21. ConVar flex_maxawaytime( "flex_maxawaytime", "1.0" );
  22. ConVar ai_debug_looktargets( "ai_debug_looktargets", "0" );
  23. ConVar ai_debug_expressions( "ai_debug_expressions", "0", FCVAR_NONE, "Show random expression decisions for NPCs." );
  24. static ConVar scene_showfaceto( "scene_showfaceto", "0", FCVAR_ARCHIVE, "When playing back, show the directions of faceto events." );
  25. BEGIN_DATADESC( CAI_BaseActor )
  26. DEFINE_FIELD( m_fLatchedPositions, FIELD_INTEGER ),
  27. DEFINE_FIELD( m_latchedEyeOrigin, FIELD_VECTOR ),
  28. DEFINE_FIELD( m_latchedEyeDirection, FIELD_VECTOR ),
  29. DEFINE_FIELD( m_latchedHeadDirection, FIELD_VECTOR ),
  30. DEFINE_FIELD( m_goalHeadDirection, FIELD_VECTOR ),
  31. DEFINE_FIELD( m_goalHeadInfluence, FIELD_FLOAT ),
  32. DEFINE_FIELD( m_goalSpineYaw, FIELD_FLOAT ),
  33. DEFINE_FIELD( m_goalBodyYaw, FIELD_FLOAT ),
  34. DEFINE_FIELD( m_goalHeadCorrection, FIELD_VECTOR ),
  35. DEFINE_FIELD( m_flBlinktime, FIELD_TIME ),
  36. DEFINE_FIELD( m_hLookTarget, FIELD_EHANDLE ),
  37. DEFINE_UTLVECTOR( m_lookQueue, FIELD_EMBEDDED ),
  38. DEFINE_UTLVECTOR( m_randomLookQueue, FIELD_EMBEDDED ),
  39. DEFINE_UTLVECTOR( m_syntheticLookQueue, FIELD_EMBEDDED ),
  40. DEFINE_FIELD( m_flNextRandomLookTime, FIELD_TIME ),
  41. DEFINE_FIELD( m_iszExpressionScene, FIELD_STRING ),
  42. DEFINE_FIELD( m_hExpressionSceneEnt, FIELD_EHANDLE ),
  43. DEFINE_FIELD( m_flNextRandomExpressionTime, FIELD_TIME ),
  44. DEFINE_FIELD( m_iszIdleExpression, FIELD_STRING ),
  45. DEFINE_FIELD( m_iszAlertExpression, FIELD_STRING ),
  46. DEFINE_FIELD( m_iszCombatExpression, FIELD_STRING ),
  47. DEFINE_FIELD( m_iszDeathExpression, FIELD_STRING ),
  48. //DEFINE_FIELD( m_ParameterBodyTransY, FIELD_INTEGER ),
  49. //DEFINE_FIELD( m_ParameterBodyTransX, FIELD_INTEGER ),
  50. //DEFINE_FIELD( m_ParameterBodyLift, FIELD_INTEGER ),
  51. DEFINE_FIELD( m_ParameterBodyYaw, FIELD_INTEGER ),
  52. //DEFINE_FIELD( m_ParameterBodyPitch, FIELD_INTEGER ),
  53. //DEFINE_FIELD( m_ParameterBodyRoll, FIELD_INTEGER ),
  54. DEFINE_FIELD( m_ParameterSpineYaw, FIELD_INTEGER ),
  55. //DEFINE_FIELD( m_ParameterSpinePitch, FIELD_INTEGER ),
  56. //DEFINE_FIELD( m_ParameterSpineRoll, FIELD_INTEGER ),
  57. DEFINE_FIELD( m_ParameterNeckTrans, FIELD_INTEGER ),
  58. DEFINE_FIELD( m_ParameterHeadYaw, FIELD_INTEGER ),
  59. DEFINE_FIELD( m_ParameterHeadPitch, FIELD_INTEGER ),
  60. DEFINE_FIELD( m_ParameterHeadRoll, FIELD_INTEGER ),
  61. //DEFINE_FIELD( m_FlexweightMoveRightLeft, FIELD_INTEGER ),
  62. //DEFINE_FIELD( m_FlexweightMoveForwardBack, FIELD_INTEGER ),
  63. //DEFINE_FIELD( m_FlexweightMoveUpDown, FIELD_INTEGER ),
  64. DEFINE_FIELD( m_FlexweightBodyRightLeft, FIELD_INTEGER ),
  65. //DEFINE_FIELD( m_FlexweightBodyUpDown, FIELD_INTEGER ),
  66. //DEFINE_FIELD( m_FlexweightBodyTilt, FIELD_INTEGER ),
  67. DEFINE_FIELD( m_FlexweightChestRightLeft, FIELD_INTEGER ),
  68. //DEFINE_FIELD( m_FlexweightChestUpDown, FIELD_INTEGER ),
  69. //DEFINE_FIELD( m_FlexweightChestTilt, FIELD_INTEGER ),
  70. DEFINE_FIELD( m_FlexweightHeadForwardBack, FIELD_INTEGER ),
  71. DEFINE_FIELD( m_FlexweightHeadRightLeft, FIELD_INTEGER ),
  72. DEFINE_FIELD( m_FlexweightHeadUpDown, FIELD_INTEGER ),
  73. DEFINE_FIELD( m_FlexweightHeadTilt, FIELD_INTEGER ),
  74. DEFINE_FIELD( m_ParameterGestureHeight, FIELD_INTEGER ),
  75. DEFINE_FIELD( m_ParameterGestureWidth, FIELD_INTEGER ),
  76. DEFINE_FIELD( m_FlexweightGestureUpDown, FIELD_INTEGER ),
  77. DEFINE_FIELD( m_FlexweightGestureRightLeft, FIELD_INTEGER ),
  78. DEFINE_FIELD( m_flAccumYawDelta, FIELD_FLOAT ),
  79. DEFINE_FIELD( m_flAccumYawScale, FIELD_FLOAT ),
  80. DEFINE_ARRAY( m_flextarget, FIELD_FLOAT, 64 ),
  81. DEFINE_KEYFIELD( m_bDontUseSemaphore, FIELD_BOOLEAN, "DontUseSpeechSemaphore" ),
  82. DEFINE_KEYFIELD( m_iszExpressionOverride, FIELD_STRING, "ExpressionOverride" ),
  83. DEFINE_EMBEDDEDBYREF( m_pExpresser ),
  84. DEFINE_INPUTFUNC( FIELD_STRING, "SetExpressionOverride", InputSetExpressionOverride ),
  85. END_DATADESC()
  86. BEGIN_SIMPLE_DATADESC( CAI_InterestTarget_t )
  87. DEFINE_FIELD( m_eType, FIELD_INTEGER ),
  88. DEFINE_FIELD( m_hTarget, FIELD_EHANDLE ),
  89. DEFINE_FIELD( m_vecPosition, FIELD_POSITION_VECTOR ),
  90. DEFINE_FIELD( m_flStartTime, FIELD_TIME ),
  91. DEFINE_FIELD( m_flEndTime, FIELD_TIME ),
  92. DEFINE_FIELD( m_flRamp, FIELD_FLOAT ),
  93. DEFINE_FIELD( m_flInterest, FIELD_FLOAT ),
  94. END_DATADESC()
  95. //-----------------------------------------------------------------------------
  96. // Purpose: clear out latched state
  97. //-----------------------------------------------------------------------------
  98. void CAI_BaseActor::StudioFrameAdvance ()
  99. {
  100. // clear out head and eye latched values
  101. m_fLatchedPositions &= ~(HUMANOID_LATCHED_ALL);
  102. BaseClass::StudioFrameAdvance();
  103. }
  104. void CAI_BaseActor::Precache()
  105. {
  106. BaseClass::Precache();
  107. if ( NULL_STRING != m_iszExpressionOverride )
  108. {
  109. PrecacheInstancedScene( STRING( m_iszExpressionOverride ) );
  110. }
  111. if ( m_iszIdleExpression != NULL_STRING )
  112. {
  113. PrecacheInstancedScene( STRING(m_iszIdleExpression ) );
  114. }
  115. if ( m_iszCombatExpression != NULL_STRING )
  116. {
  117. PrecacheInstancedScene( STRING(m_iszCombatExpression ) );
  118. }
  119. if ( m_iszAlertExpression != NULL_STRING )
  120. {
  121. PrecacheInstancedScene( STRING(m_iszAlertExpression) );
  122. }
  123. if ( m_iszDeathExpression != NULL_STRING )
  124. {
  125. PrecacheInstancedScene( STRING(m_iszDeathExpression) );
  126. }
  127. }
  128. static char const *g_ServerSideFlexControllers[] =
  129. {
  130. "body_rightleft",
  131. //"body_updown",
  132. //"body_tilt",
  133. "chest_rightleft",
  134. //"chest_updown",
  135. //"chest_tilt",
  136. "head_forwardback",
  137. "head_rightleft",
  138. "head_updown",
  139. "head_tilt",
  140. "gesture_updown",
  141. "gesture_rightleft"
  142. };
  143. //-----------------------------------------------------------------------------
  144. // Purpose: Static method
  145. // Input : *szName -
  146. // Output : Returns true on success, false on failure.
  147. //-----------------------------------------------------------------------------
  148. bool CAI_BaseActor::IsServerSideFlexController( char const *szName )
  149. {
  150. int c = ARRAYSIZE( g_ServerSideFlexControllers );
  151. for ( int i = 0; i < c; ++i )
  152. {
  153. if ( !Q_stricmp( szName, g_ServerSideFlexControllers[ i ] ) )
  154. return true;
  155. }
  156. return false;
  157. }
  158. void CAI_BaseActor::SetModel( const char *szModelName )
  159. {
  160. BaseClass::SetModel( szModelName );
  161. //Init( m_ParameterBodyTransY, "body_trans_Y" );
  162. //Init( m_ParameterBodyTransX, "body_trans_X" );
  163. //Init( m_ParameterBodyLift, "body_lift" );
  164. Init( m_ParameterBodyYaw, "body_yaw" );
  165. //Init( m_ParameterBodyPitch, "body_pitch" );
  166. //Init( m_ParameterBodyRoll, "body_roll" );
  167. Init( m_ParameterSpineYaw, "spine_yaw" );
  168. //Init( m_ParameterSpinePitch, "spine_pitch" );
  169. //Init( m_ParameterSpineRoll, "spine_roll" );
  170. Init( m_ParameterNeckTrans, "neck_trans" );
  171. Init( m_ParameterHeadYaw, "head_yaw" );
  172. Init( m_ParameterHeadPitch, "head_pitch" );
  173. Init( m_ParameterHeadRoll, "head_roll" );
  174. //Init( m_FlexweightMoveRightLeft, "move_rightleft" );
  175. //Init( m_FlexweightMoveForwardBack, "move_forwardback" );
  176. //Init( m_FlexweightMoveUpDown, "move_updown" );
  177. Init( m_FlexweightBodyRightLeft, "body_rightleft" );
  178. //Init( m_FlexweightBodyUpDown, "body_updown" );
  179. //Init( m_FlexweightBodyTilt, "body_tilt" );
  180. Init( m_FlexweightChestRightLeft, "chest_rightleft" );
  181. //Init( m_FlexweightChestUpDown, "chest_updown" );
  182. //Init( m_FlexweightChestTilt, "chest_tilt" );
  183. Init( m_FlexweightHeadForwardBack, "head_forwardback" );
  184. Init( m_FlexweightHeadRightLeft, "head_rightleft" );
  185. Init( m_FlexweightHeadUpDown, "head_updown" );
  186. Init( m_FlexweightHeadTilt, "head_tilt" );
  187. Init( m_ParameterGestureHeight, "gesture_height" );
  188. Init( m_ParameterGestureWidth, "gesture_width" );
  189. Init( m_FlexweightGestureUpDown, "gesture_updown" );
  190. Init( m_FlexweightGestureRightLeft, "gesture_rightleft" );
  191. }
  192. //-----------------------------------------------------------------------------
  193. // Purpose:
  194. //-----------------------------------------------------------------------------
  195. bool CAI_BaseActor::StartSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event, CChoreoActor *actor, CBaseEntity *pTarget )
  196. {
  197. Assert( info );
  198. Assert( info->m_pScene );
  199. Assert( info->m_pEvent );
  200. // FIXME: this code looks duplicated
  201. switch ( info->m_pEvent->GetType() )
  202. {
  203. case CChoreoEvent::FACE:
  204. {
  205. return BaseClass::StartSceneEvent( info, scene, event, actor, pTarget );
  206. }
  207. break;
  208. case CChoreoEvent::GENERIC:
  209. {
  210. if (stricmp( event->GetParameters(), "AI_BLINK") == 0)
  211. {
  212. info->m_nType = SCENE_AI_BLINK;
  213. // blink eyes
  214. Blink();
  215. // don't blink for duration, or next random blink time
  216. float flDuration = (event->GetEndTime() - scene->GetTime());
  217. m_flBlinktime = gpGlobals->curtime + MAX( flDuration, random->RandomFloat( 1.5, 4.5 ) );
  218. }
  219. else if (stricmp( event->GetParameters(), "AI_HOLSTER") == 0)
  220. {
  221. // FIXME: temp code for test
  222. info->m_nType = SCENE_AI_HOLSTER;
  223. info->m_iLayer = HolsterWeapon();
  224. return true;
  225. }
  226. else if (stricmp( event->GetParameters(), "AI_UNHOLSTER") == 0)
  227. {
  228. // FIXME: temp code for test
  229. info->m_nType = SCENE_AI_UNHOLSTER;
  230. info->m_iLayer = UnholsterWeapon();
  231. return true;
  232. }
  233. else if (stricmp( event->GetParameters(), "AI_AIM") == 0)
  234. {
  235. info->m_nType = SCENE_AI_AIM;
  236. info->m_hTarget = pTarget;
  237. }
  238. else if (stricmp( event->GetParameters(), "AI_RANDOMLOOK") == 0)
  239. {
  240. info->m_nType = SCENE_AI_RANDOMLOOK;
  241. info->m_flNext = 0.0;
  242. }
  243. else if (stricmp( event->GetParameters(), "AI_RANDOMFACEFLEX") == 0)
  244. {
  245. info->m_nType = SCENE_AI_RANDOMFACEFLEX;
  246. info->m_flNext = 0.0;
  247. info->InitWeight( this );
  248. }
  249. else if (stricmp( event->GetParameters(), "AI_RANDOMHEADFLEX") == 0)
  250. {
  251. info->m_nType = SCENE_AI_RANDOMHEADFLEX;
  252. info->m_flNext = 0.0;
  253. }
  254. else if (stricmp( event->GetParameters(), "AI_IGNORECOLLISION") == 0)
  255. {
  256. CBaseEntity *pTarget = FindNamedEntity( event->GetParameters2( ) );
  257. if (pTarget)
  258. {
  259. info->m_nType = SCENE_AI_IGNORECOLLISION;
  260. info->m_hTarget = pTarget;
  261. float remaining = event->GetEndTime() - scene->GetTime();
  262. NPCPhysics_CreateSolver( this, pTarget, true, remaining );
  263. info->m_flNext = gpGlobals->curtime + remaining;
  264. return true;
  265. }
  266. else
  267. {
  268. Warning( "CSceneEntity %s unable to find actor named \"%s\"\n", scene->GetFilename(), event->GetParameters2() );
  269. return false;
  270. }
  271. }
  272. else if (stricmp( event->GetParameters(), "AI_DISABLEAI") == 0)
  273. {
  274. info->m_nType = SCENE_AI_DISABLEAI;
  275. }
  276. else
  277. {
  278. return BaseClass::StartSceneEvent( info, scene, event, actor, pTarget );
  279. }
  280. return true;
  281. }
  282. break;
  283. default:
  284. return BaseClass::StartSceneEvent( info, scene, event, actor, pTarget );
  285. }
  286. }
  287. bool CAI_BaseActor::ProcessSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event )
  288. {
  289. Assert( info );
  290. Assert( info->m_pScene );
  291. Assert( info->m_pEvent );
  292. // FIXME: this code looks duplicated
  293. switch ( info->m_pEvent->GetType() )
  294. {
  295. case CChoreoEvent::FACE:
  296. {
  297. // make sure target exists
  298. if (info->m_hTarget == NULL)
  299. return false;
  300. bool bInScene = false;
  301. // lockbodyfacing is designed to run on top of both normal AI and on top of
  302. // scripted_sequences. By allowing torso turns during post-idles, pre-idles,
  303. // act-busy's, scripted_sequences, normal AI movements, etc., it increases
  304. // the functionality of those AI features without breaking their assuptions
  305. // that the entity won't be made to "turn" by something outside of those
  306. // AI's control.
  307. // lockbody facing is also usefull when npcs are moving and you want them to turn
  308. // towards something but still walk in the direction of travel.
  309. if (!event->IsLockBodyFacing())
  310. bInScene = EnterSceneSequence( scene, event, true );
  311. // make sure we're still able to play this command
  312. if (!info->m_bStarted)
  313. {
  314. info->m_flInitialYaw = GetLocalAngles().y;
  315. info->m_flTargetYaw = info->m_flInitialYaw;
  316. info->m_flFacingYaw = info->m_flInitialYaw;
  317. if (IsMoving())
  318. {
  319. info->m_flWeight = 1.0;
  320. }
  321. else
  322. {
  323. info->m_flWeight = 0.0;
  324. }
  325. }
  326. // lock in place if aiming at self
  327. if (info->m_hTarget == this)
  328. {
  329. return true;
  330. }
  331. if (!bInScene || info->m_bIsMoving != IsMoving())
  332. {
  333. info->m_flInitialYaw = GetLocalAngles().y;
  334. }
  335. info->m_bIsMoving = IsMoving();
  336. // Msg("%f : %f - %f\n", scene->GetTime(), event->GetStartTime(), event->GetEndTime() );
  337. float flTime = clamp( scene->GetTime(), event->GetStartTime(), event->GetEndTime() - 0.1f );
  338. float intensity = event->GetIntensity( flTime );
  339. // clamp in-ramp to 0.5 seconds
  340. float flDuration = scene->GetTime() - event->GetStartTime();
  341. float flMaxIntensity = flDuration < 0.5f ? SimpleSpline( flDuration / 0.5f ) : 1.0f;
  342. intensity = clamp( intensity, 0.0f, flMaxIntensity );
  343. if (bInScene && info->m_bIsMoving)
  344. {
  345. info->m_flInitialYaw = GetLocalAngles().y;
  346. }
  347. if (!event->IsLockBodyFacing())
  348. {
  349. if (!info->m_bIsMoving && bInScene)
  350. {
  351. AccumulateIdealYaw( info->m_flFacingYaw, intensity );
  352. }
  353. }
  354. float diff;
  355. float dir;
  356. float flSpineYaw;
  357. float flBodyYaw;
  358. // move upper body to account for missing body yaw
  359. diff = UTIL_AngleDiff( info->m_flTargetYaw, GetLocalAngles().y );
  360. if (diff < 0)
  361. {
  362. diff = -diff;
  363. dir = -1;
  364. }
  365. else
  366. {
  367. dir = 1;
  368. }
  369. flSpineYaw = MIN( diff, 30 );
  370. flBodyYaw = MIN( diff - flSpineYaw, 30 );
  371. m_goalSpineYaw = m_goalSpineYaw * (1.0 - intensity) + intensity * flSpineYaw * dir;
  372. m_goalBodyYaw = m_goalBodyYaw * (1.0 - intensity) + intensity * flBodyYaw * dir;
  373. /*
  374. NDebugOverlay::YawArrow( GetAbsOrigin(), GetLocalAngles().y, 64, 16, 255, 255, 255, 0, true, 0.1 );
  375. NDebugOverlay::YawArrow( GetAbsOrigin() + Vector( 0, 0, 8 ), GetLocalAngles().y + m_goalBodyYaw, 64, 16, 255, 128, 128, 0, true, 0.1 );
  376. NDebugOverlay::YawArrow( GetAbsOrigin() + Vector( 0, 0, 16 ), GetLocalAngles().y + m_goalSpineYaw, 64, 16, 128, 255, 128, 0, true, 0.1 );
  377. NDebugOverlay::YawArrow( GetAbsOrigin() + Vector( 0, 0, 24 ), info->m_flTargetYaw, 64, 16, 128, 128, 255, 0, true, 0.1 );
  378. */
  379. CAI_BaseNPC *pGoalNpc = info->m_hTarget->MyNPCPointer();
  380. float goalYaw = GetLocalAngles().y;
  381. if ( pGoalNpc )
  382. {
  383. goalYaw = CalcIdealYaw( pGoalNpc->FacingPosition() );
  384. }
  385. else
  386. {
  387. goalYaw = CalcIdealYaw( info->m_hTarget->EyePosition() );
  388. }
  389. if (developer.GetInt() > 0 && scene_showfaceto.GetBool())
  390. {
  391. NDebugOverlay::YawArrow( GetAbsOrigin() + Vector( 0, 0, 1 ), goalYaw, 8 + 32 * intensity, 8, 255, 255, 255, 0, true, 0.12 );
  392. }
  393. diff = UTIL_AngleDiff( goalYaw, info->m_flInitialYaw ) * intensity;
  394. dir = 1.0;
  395. // debounce delta a bit
  396. info->m_flTargetYaw = UTIL_AngleMod( info->m_flInitialYaw + diff );
  397. if (diff < 0)
  398. {
  399. diff = -diff;
  400. dir = -1;
  401. }
  402. // calc how much to use the spine for turning
  403. float spineintensity = (1.0 - MAX( 0.0, (intensity - 0.5) / 0.5 ));
  404. // force spine to full if not in scene or locked
  405. if (!bInScene || event->IsLockBodyFacing() )
  406. {
  407. spineintensity = 1.0;
  408. }
  409. flSpineYaw = MIN( diff * spineintensity, 30 );
  410. flBodyYaw = MIN( diff * spineintensity - flSpineYaw, 30 );
  411. info->m_flFacingYaw = info->m_flInitialYaw + (diff - flBodyYaw - flSpineYaw) * dir;
  412. if (!event->IsLockBodyFacing())
  413. {
  414. AddFacingTarget( info->m_hTarget, intensity, 0.2 ); // facing targets are lagged by one frame
  415. }
  416. return true;
  417. }
  418. case CChoreoEvent::GENERIC:
  419. {
  420. switch(info->m_nType)
  421. {
  422. case SCENE_AI_BLINK:
  423. {
  424. // keep eyes not blinking for duration
  425. float flDuration = (event->GetEndTime() - scene->GetTime());
  426. m_flBlinktime = MAX( m_flBlinktime, gpGlobals->curtime + flDuration );
  427. }
  428. return true;
  429. case SCENE_AI_HOLSTER:
  430. {
  431. }
  432. return true;
  433. case SCENE_AI_UNHOLSTER:
  434. {
  435. }
  436. return true;
  437. case SCENE_AI_AIM:
  438. {
  439. if ( info->m_hTarget )
  440. {
  441. Vector vecAimTargetLoc = info->m_hTarget->EyePosition();
  442. Vector vecAimDir = vecAimTargetLoc - EyePosition();
  443. VectorNormalize( vecAimDir );
  444. SetAim( vecAimDir);
  445. }
  446. }
  447. return true;
  448. case SCENE_AI_RANDOMLOOK:
  449. {
  450. if (info->m_flNext < gpGlobals->curtime)
  451. {
  452. info->m_flNext = gpGlobals->curtime + PickLookTarget( m_syntheticLookQueue ) - 0.4;
  453. if (m_syntheticLookQueue.Count() > 0)
  454. {
  455. float flDuration = (event->GetEndTime() - scene->GetTime());
  456. int i = m_syntheticLookQueue.Count() - 1;
  457. m_syntheticLookQueue[i].m_flEndTime = MIN( m_syntheticLookQueue[i].m_flEndTime, gpGlobals->curtime + flDuration );
  458. m_syntheticLookQueue[i].m_flInterest = 0.1;
  459. }
  460. }
  461. }
  462. return true;
  463. case SCENE_AI_RANDOMFACEFLEX:
  464. return RandomFaceFlex( info, scene, event );
  465. case SCENE_AI_RANDOMHEADFLEX:
  466. return true;
  467. case SCENE_AI_IGNORECOLLISION:
  468. if (info->m_hTarget && info->m_flNext < gpGlobals->curtime)
  469. {
  470. float remaining = event->GetEndTime() - scene->GetTime();
  471. NPCPhysics_CreateSolver( this, info->m_hTarget, true, remaining );
  472. info->m_flNext = gpGlobals->curtime + remaining;
  473. }
  474. // FIXME: needs to handle scene pause
  475. return true;
  476. case SCENE_AI_DISABLEAI:
  477. if (!(GetState() == NPC_STATE_SCRIPT || IsCurSchedule( SCHED_SCENE_GENERIC )) )
  478. {
  479. EnterSceneSequence( scene, event );
  480. }
  481. return true;
  482. default:
  483. return false;
  484. }
  485. }
  486. break;
  487. default:
  488. return BaseClass::ProcessSceneEvent( info, scene, event );
  489. }
  490. }
  491. bool CAI_BaseActor::RandomFaceFlex( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event )
  492. {
  493. if (info->m_flNext < gpGlobals->curtime)
  494. {
  495. const flexsettinghdr_t *pSettinghdr = ( const flexsettinghdr_t * )FindSceneFile( event->GetParameters2() );
  496. if (pSettinghdr == NULL)
  497. {
  498. pSettinghdr = ( const flexsettinghdr_t * )FindSceneFile( "random" );
  499. }
  500. if ( pSettinghdr )
  501. {
  502. info->m_flNext = gpGlobals->curtime + random->RandomFloat( 0.3, 0.5 ) * (30.0 / pSettinghdr->numflexsettings);
  503. flexsetting_t const *pSetting = NULL;
  504. pSetting = pSettinghdr->pSetting( random->RandomInt( 0, pSettinghdr->numflexsettings - 1 ) );
  505. flexweight_t *pWeights = NULL;
  506. int truecount = pSetting->psetting( (byte *)pSettinghdr, 0, &pWeights );
  507. if ( !pWeights )
  508. return false;
  509. int i;
  510. for (i = 0; i < truecount; i++, pWeights++)
  511. {
  512. // Translate to local flex controller
  513. // this is translating from the settings's local index to the models local index
  514. int index = FlexControllerLocalToGlobal( pSettinghdr, pWeights->key );
  515. // FIXME: this is supposed to blend based on pWeight->influence, but the order is wrong...
  516. // float value = GetFlexWeight( index ) * (1 - scale * pWeights->influence) + scale * pWeights->weight;
  517. // Add scaled weighting in to total
  518. m_flextarget[ index ] = pWeights->weight;
  519. }
  520. }
  521. else
  522. {
  523. return false;
  524. }
  525. }
  526. // adjust intensity if this is a background scene and there's other flex animations playing
  527. float intensity = info->UpdateWeight( this ) * event->GetIntensity( scene->GetTime() );
  528. // slide it up.
  529. for (LocalFlexController_t i = LocalFlexController_t(0); i < GetNumFlexControllers(); i++)
  530. {
  531. float weight = GetFlexWeight( i );
  532. if (weight != m_flextarget[i])
  533. {
  534. float delta = (m_flextarget[i] - weight) / random->RandomFloat( 2.0, 4.0 );
  535. weight = weight + delta * intensity;
  536. }
  537. weight = clamp( weight, 0.0f, 1.0f );
  538. SetFlexWeight( i, weight );
  539. }
  540. return true;
  541. }
  542. bool CAI_BaseActor::ClearSceneEvent( CSceneEventInfo *info, bool fastKill, bool canceled )
  543. {
  544. Assert( info );
  545. Assert( info->m_pScene );
  546. Assert( info->m_pEvent );
  547. // FIXME: this code looks duplicated
  548. switch ( info->m_pEvent->GetType() )
  549. {
  550. case CChoreoEvent::FACE:
  551. {
  552. return BaseClass::ClearSceneEvent( info, fastKill, canceled );
  553. }
  554. break;
  555. default:
  556. return BaseClass::ClearSceneEvent( info, fastKill, canceled );
  557. }
  558. }
  559. bool CAI_BaseActor::CheckSceneEventCompletion( CSceneEventInfo *info, float currenttime, CChoreoScene *scene, CChoreoEvent *event )
  560. {
  561. Assert( info );
  562. Assert( info->m_pScene );
  563. Assert( info->m_pEvent );
  564. switch ( event->GetType() )
  565. {
  566. case CChoreoEvent::GENERIC:
  567. {
  568. switch( info->m_nType)
  569. {
  570. case SCENE_AI_HOLSTER:
  571. case SCENE_AI_UNHOLSTER:
  572. {
  573. if (info->m_iLayer == -1)
  574. {
  575. return true;
  576. }
  577. float preload = event->GetEndTime() - currenttime;
  578. if (preload < 0)
  579. {
  580. return true;
  581. }
  582. float t = (1.0 - GetLayerCycle( info->m_iLayer )) * SequenceDuration( GetLayerSequence( info->m_iLayer ) );
  583. return (t <= preload);
  584. }
  585. }
  586. }
  587. }
  588. return BaseClass::CheckSceneEventCompletion( info, currenttime, scene, event );
  589. }
  590. //-----------------------------------------------------------------------------
  591. // Purpose: clear out latched state
  592. //-----------------------------------------------------------------------------
  593. void CAI_BaseActor::SetViewtarget( const Vector &viewtarget )
  594. {
  595. // clear out eye latch
  596. m_fLatchedPositions &= ~HUMANOID_LATCHED_EYE;
  597. BaseClass::SetViewtarget( viewtarget );
  598. }
  599. //-----------------------------------------------------------------------------
  600. // Purpose: Returns true position of the eyeballs
  601. //-----------------------------------------------------------------------------
  602. void CAI_BaseActor::UpdateLatchedValues( )
  603. {
  604. if (!(m_fLatchedPositions & HUMANOID_LATCHED_HEAD))
  605. {
  606. // set head latch
  607. m_fLatchedPositions |= HUMANOID_LATCHED_HEAD;
  608. if (!HasCondition( COND_IN_PVS ) || !GetAttachment( "eyes", m_latchedEyeOrigin, &m_latchedHeadDirection ))
  609. {
  610. m_latchedEyeOrigin = BaseClass::EyePosition( );
  611. AngleVectors( GetLocalAngles(), &m_latchedHeadDirection );
  612. }
  613. // clear out eye latch
  614. m_fLatchedPositions &= ~(HUMANOID_LATCHED_EYE);
  615. // DevMsg( "eyeball %4f %4f %4f : %3f %3f %3f\n", origin.x, origin.y, origin.z, angles.x, angles.y, angles.z );
  616. }
  617. if (!(m_fLatchedPositions & HUMANOID_LATCHED_EYE))
  618. {
  619. m_fLatchedPositions |= HUMANOID_LATCHED_EYE;
  620. if ( CapabilitiesGet() & bits_CAP_ANIMATEDFACE )
  621. {
  622. m_latchedEyeDirection = GetViewtarget() - m_latchedEyeOrigin;
  623. VectorNormalize( m_latchedEyeDirection );
  624. }
  625. else
  626. {
  627. m_latchedEyeDirection = m_latchedHeadDirection;
  628. }
  629. }
  630. }
  631. //-----------------------------------------------------------------------------
  632. // Purpose: Returns true position of the eyeballs
  633. //-----------------------------------------------------------------------------
  634. Vector CAI_BaseActor::EyePosition( )
  635. {
  636. UpdateLatchedValues();
  637. return m_latchedEyeOrigin;
  638. }
  639. #define MIN_LOOK_TARGET_DIST 1.0f
  640. #define MAX_FULL_LOOK_TARGET_DIST 10.0f
  641. //-----------------------------------------------------------------------------
  642. // Purpose: Returns true if target is in legal range of eye movement for the current head position
  643. //-----------------------------------------------------------------------------
  644. bool CAI_BaseActor::ValidEyeTarget(const Vector &lookTargetPos)
  645. {
  646. Vector vHeadDir = HeadDirection3D( );
  647. Vector lookTargetDir = lookTargetPos - EyePosition();
  648. float flDist = VectorNormalize(lookTargetDir);
  649. if (flDist < MIN_LOOK_TARGET_DIST)
  650. {
  651. return false;
  652. }
  653. // Only look if it doesn't crank my eyeballs too far
  654. float dotPr = DotProduct(lookTargetDir, vHeadDir);
  655. // DevMsg( "ValidEyeTarget( %4f %4f %4f ) %3f\n", lookTargetPos.x, lookTargetPos.y, lookTargetPos.z, dotPr );
  656. if (dotPr > 0.259) // +- 75 degrees
  657. // if (dotPr > 0.86) // +- 30 degrees
  658. {
  659. return true;
  660. }
  661. return false;
  662. }
  663. //-----------------------------------------------------------------------------
  664. // Purpose: Returns true if target is in legal range of possible head movements
  665. //-----------------------------------------------------------------------------
  666. bool CAI_BaseActor::ValidHeadTarget(const Vector &lookTargetPos)
  667. {
  668. Vector vFacing = BodyDirection3D();
  669. Vector lookTargetDir = lookTargetPos - EyePosition();
  670. float flDist = VectorNormalize(lookTargetDir);
  671. if (flDist < MIN_LOOK_TARGET_DIST)
  672. {
  673. return false;
  674. }
  675. // Only look if it doesn't crank my head too far
  676. float dotPr = DotProduct(lookTargetDir, vFacing);
  677. if (dotPr > 0 && fabs( lookTargetDir.z ) < 0.7) // +- 90 degrees side to side, +- 45 up/down
  678. {
  679. return true;
  680. }
  681. return false;
  682. }
  683. //-----------------------------------------------------------------------------
  684. // Purpose: Returns how much to try to look at the target
  685. //-----------------------------------------------------------------------------
  686. float CAI_BaseActor::HeadTargetValidity(const Vector &lookTargetPos)
  687. {
  688. Vector vFacing = BodyDirection3D();
  689. int iForward = LookupAttachment( "forward" );
  690. if ( iForward > 0 )
  691. {
  692. Vector tmp1;
  693. GetAttachment( iForward, tmp1, &vFacing, NULL, NULL );
  694. }
  695. Vector lookTargetDir = lookTargetPos - EyePosition();
  696. float flDist = lookTargetDir.Length2D();
  697. VectorNormalize(lookTargetDir);
  698. if (flDist <= MIN_LOOK_TARGET_DIST)
  699. {
  700. return 0;
  701. }
  702. // Only look if it doesn't crank my head too far
  703. float dotPr = DotProduct(lookTargetDir, vFacing);
  704. // only look if target is within +-135 degrees
  705. // scale 1..-0.707 == 1..1, -.707..-1 == 1..0
  706. // X * b + b = 1 == 1 / (X + 1) = b, 3.4142
  707. float flInterest = clamp( 3.4142f + 3.4142f * dotPr, 0.f, 1.f );
  708. // stop looking when point too close
  709. if (flDist < MAX_FULL_LOOK_TARGET_DIST)
  710. {
  711. flInterest = flInterest * (flDist - MIN_LOOK_TARGET_DIST ) / (MAX_FULL_LOOK_TARGET_DIST - MIN_LOOK_TARGET_DIST);
  712. }
  713. return flInterest;
  714. }
  715. //-----------------------------------------------------------------------------
  716. // Purpose: Integrate head turn over time
  717. //-----------------------------------------------------------------------------
  718. void CAI_BaseActor::SetHeadDirection( const Vector &vTargetPos, float flInterval)
  719. {
  720. Assert(0); // Actors shouldn't be calling this, it doesn't do anything
  721. }
  722. float CAI_BaseActor::ClampWithBias( PoseParameter_t index, float value, float base )
  723. {
  724. return EdgeLimitPoseParameter( (int)index, value, base );
  725. }
  726. //-----------------------------------------------------------------------------
  727. // Purpose: Accumulate all the wanted yaw changes
  728. //-----------------------------------------------------------------------------
  729. void CAI_BaseActor::AccumulateIdealYaw( float flYaw, float flIntensity )
  730. {
  731. float diff = AngleDiff( flYaw, GetLocalAngles().y );
  732. m_flAccumYawDelta += diff * flIntensity;
  733. m_flAccumYawScale += flIntensity;
  734. }
  735. //-----------------------------------------------------------------------------
  736. // Purpose: do any pending yaw movements
  737. //-----------------------------------------------------------------------------
  738. bool CAI_BaseActor::SetAccumulatedYawAndUpdate( void )
  739. {
  740. if (m_flAccumYawScale > 0.0)
  741. {
  742. float diff = m_flAccumYawDelta / m_flAccumYawScale;
  743. float facing = GetLocalAngles().y + diff;
  744. m_flAccumYawDelta = 0.0;
  745. m_flAccumYawScale = 0.0;
  746. if (IsCurSchedule( SCHED_SCENE_GENERIC ))
  747. {
  748. if (!IsMoving())
  749. {
  750. GetMotor()->SetIdealYawAndUpdate( facing );
  751. return true;
  752. }
  753. }
  754. }
  755. return false;
  756. }
  757. //-----------------------------------------------------------------------------
  758. // Purpose: match actors "forward" attachment to point in direction of vHeadTarget
  759. //-----------------------------------------------------------------------------
  760. void CAI_BaseActor::UpdateBodyControl( )
  761. {
  762. // FIXME: only during idle, or in response to accel/decel
  763. //Set( m_ParameterBodyTransY, Get( m_FlexweightMoveRightLeft ) );
  764. //Set( m_ParameterBodyTransX, Get( m_FlexweightMoveForwardBack ) );
  765. //Set( m_ParameterBodyLift, Get( m_FlexweightMoveUpDown ) );
  766. Set( m_ParameterBodyYaw, Get( m_FlexweightBodyRightLeft ) + m_goalBodyYaw );
  767. //Set( m_ParameterBodyPitch, Get( m_FlexweightBodyUpDown ) );
  768. //Set( m_ParameterBodyRoll, Get( m_FlexweightBodyTilt ) );
  769. Set( m_ParameterSpineYaw, Get( m_FlexweightChestRightLeft ) + m_goalSpineYaw );
  770. //Set( m_ParameterSpinePitch, Get( m_FlexweightChestUpDown ) );
  771. //Set( m_ParameterSpineRoll, Get( m_FlexweightChestTilt ) );
  772. Set( m_ParameterNeckTrans, Get( m_FlexweightHeadForwardBack ) );
  773. }
  774. static ConVar scene_clamplookat( "scene_clamplookat", "1", FCVAR_NONE, "Clamp head turns to a max of 20 degrees per think." );
  775. void CAI_BaseActor::UpdateHeadControl( const Vector &vHeadTarget, float flHeadInfluence )
  776. {
  777. float flTarget;
  778. float flLimit;
  779. if (!(CapabilitiesGet() & bits_CAP_TURN_HEAD))
  780. {
  781. return;
  782. }
  783. // calc current animation head bias, movement needs to clamp accumulated with this
  784. QAngle angBias;
  785. QAngle vTargetAngles;
  786. int iEyes = LookupAttachment( "eyes" );
  787. int iChest = LookupAttachment( "chest" );
  788. int iForward = LookupAttachment( "forward" );
  789. matrix3x4_t eyesToWorld;
  790. matrix3x4_t forwardToWorld, worldToForward;
  791. if (iEyes <= 0 || iForward <= 0)
  792. {
  793. // Head control on model without "eyes" or "forward" attachment
  794. // Most likely this is a cheaple or a generic_actor set to a model that doesn't support head/eye turning.
  795. // DevWarning( "%s using model \"%s\" that doesn't support head turning\n", GetClassname(), STRING( GetModelName() ) );
  796. CapabilitiesRemove( bits_CAP_TURN_HEAD );
  797. return;
  798. }
  799. GetAttachment( iEyes, eyesToWorld );
  800. GetAttachment( iForward, forwardToWorld );
  801. MatrixInvert( forwardToWorld, worldToForward );
  802. // Lookup chest attachment to do compounded range limit checks
  803. if (iChest > 0)
  804. {
  805. matrix3x4_t chestToWorld, worldToChest;
  806. GetAttachment( iChest, chestToWorld );
  807. MatrixInvert( chestToWorld, worldToChest );
  808. matrix3x4_t tmpM;
  809. ConcatTransforms( worldToChest, eyesToWorld, tmpM );
  810. MatrixAngles( tmpM, angBias );
  811. angBias.y -= Get( m_ParameterHeadYaw );
  812. angBias.x -= Get( m_ParameterHeadPitch );
  813. angBias.z -= Get( m_ParameterHeadRoll );
  814. /*
  815. if ( (m_debugOverlays & OVERLAY_NPC_SELECTED_BIT) )
  816. {
  817. // Msg("bias %f %f %f\n", angBias.x, angBias.y, angBias.z );
  818. Vector tmp1, tmp2;
  819. VectorTransform( Vector( 0, 0, 0), chestToWorld, tmp1 );
  820. VectorTransform( Vector( 100, 0, 0), chestToWorld, tmp2 );
  821. NDebugOverlay::Line( tmp1, tmp2, 0,0,255, false, 0.12 );
  822. VectorTransform( Vector( 0, 0, 0), eyesToWorld, tmp1 );
  823. VectorTransform( Vector( 100, 0, 0), eyesToWorld, tmp2 );
  824. NDebugOverlay::Line( tmp1, tmp2, 0,0,255, false, 0.12 );
  825. // NDebugOverlay::Line( EyePosition(), pEntity->EyePosition(), 0,0,255, false, 0.5);
  826. }
  827. */
  828. }
  829. else
  830. {
  831. angBias.Init( 0, 0, 0 );
  832. }
  833. matrix3x4_t targetXform;
  834. targetXform = forwardToWorld;
  835. Vector vTargetDir = vHeadTarget - EyePosition();
  836. if (scene_clamplookat.GetBool())
  837. {
  838. // scale down pitch when the target is behind the head
  839. Vector vTargetLocal;
  840. VectorNormalize( vTargetDir );
  841. VectorIRotate( vTargetDir, forwardToWorld, vTargetLocal );
  842. vTargetLocal.z *= clamp( vTargetLocal.x, 0.1f, 1.0f );
  843. VectorNormalize( vTargetLocal );
  844. VectorRotate( vTargetLocal, forwardToWorld, vTargetDir );
  845. // clamp local influence when target is behind the head
  846. flHeadInfluence = flHeadInfluence * clamp( vTargetLocal.x * 2.0f + 2.0f, 0.0f, 1.0f );
  847. }
  848. Studio_AlignIKMatrix( targetXform, vTargetDir );
  849. matrix3x4_t headXform;
  850. ConcatTransforms( worldToForward, targetXform, headXform );
  851. MatrixAngles( headXform, vTargetAngles );
  852. // partially debounce head goal
  853. float s0 = 1.0 - flHeadInfluence + GetHeadDebounce() * flHeadInfluence;
  854. float s1 = (1.0 - s0);
  855. // limit velocity of head turns
  856. m_goalHeadCorrection.x = UTIL_Approach( m_goalHeadCorrection.x * s0 + vTargetAngles.x * s1, m_goalHeadCorrection.x, 10.0 );
  857. m_goalHeadCorrection.y = UTIL_Approach( m_goalHeadCorrection.y * s0 + vTargetAngles.y * s1, m_goalHeadCorrection.y, 30.0 );
  858. m_goalHeadCorrection.z = UTIL_Approach( m_goalHeadCorrection.z * s0 + vTargetAngles.z * s1, m_goalHeadCorrection.z, 10.0 );
  859. /*
  860. if ( (m_debugOverlays & OVERLAY_NPC_SELECTED_BIT) )
  861. {
  862. // Msg( "yaw %.1f (%f) pitch %.1f (%.1f)\n", m_goalHeadCorrection.y, vTargetAngles.y, vTargetAngles.x, m_goalHeadCorrection.x );
  863. // Msg( "yaw %.2f (goal %.2f) (influence %.2f) (flex %.2f)\n", flLimit, m_goalHeadCorrection.y, flHeadInfluence, Get( m_FlexweightHeadRightLeft ) );
  864. }
  865. */
  866. flTarget = m_goalHeadCorrection.y + Get( m_FlexweightHeadRightLeft );
  867. flLimit = ClampWithBias( m_ParameterHeadYaw, flTarget, angBias.y );
  868. /*
  869. if ( (m_debugOverlays & OVERLAY_NPC_SELECTED_BIT) )
  870. {
  871. Msg( "yaw %5.1f : (%5.1f : %5.1f) %5.1f (%5.1f)\n", flLimit, m_goalHeadCorrection.y, Get( m_FlexweightHeadRightLeft ), angBias.y, Get( m_ParameterHeadYaw ) );
  872. }
  873. */
  874. Set( m_ParameterHeadYaw, flLimit );
  875. flTarget = m_goalHeadCorrection.x + Get( m_FlexweightHeadUpDown );
  876. flLimit = ClampWithBias( m_ParameterHeadPitch, flTarget, angBias.x );
  877. /*
  878. if ( (m_debugOverlays & OVERLAY_NPC_SELECTED_BIT) )
  879. {
  880. Msg( "pitch %5.1f : (%5.1f : %5.1f) %5.1f (%5.1f)\n", flLimit, m_goalHeadCorrection.x, Get( m_FlexweightHeadUpDown ), angBias.x, Get( m_ParameterHeadPitch ) );
  881. }
  882. */
  883. Set( m_ParameterHeadPitch, flLimit );
  884. flTarget = m_goalHeadCorrection.z + Get( m_FlexweightHeadTilt );
  885. flLimit = ClampWithBias( m_ParameterHeadRoll, flTarget, angBias.z );
  886. /*
  887. if ( (m_debugOverlays & OVERLAY_NPC_SELECTED_BIT) )
  888. {
  889. Msg( "roll %5.1f : (%5.1f : %5.1f) %5.1f (%5.1f)\n", flLimit, m_goalHeadCorrection.z, Get( m_FlexweightHeadTilt ), angBias.z, Get( m_ParameterHeadRoll ) );
  890. }
  891. */
  892. Set( m_ParameterHeadRoll, flLimit );
  893. }
  894. //------------------------------------------------------------------------------
  895. // Purpose : Calculate the NPC's eye direction in 2D world space
  896. // Input :
  897. // Output :
  898. //------------------------------------------------------------------------------
  899. Vector CAI_BaseActor::EyeDirection2D( void )
  900. {
  901. Vector vEyeDirection = EyeDirection3D( );
  902. vEyeDirection.z = 0;
  903. vEyeDirection.AsVector2D().NormalizeInPlace();
  904. return vEyeDirection;
  905. }
  906. //------------------------------------------------------------------------------
  907. // Purpose : Calculate the NPC's eye direction in 2D world space
  908. // Input :
  909. // Output :
  910. //------------------------------------------------------------------------------
  911. Vector CAI_BaseActor::EyeDirection3D( void )
  912. {
  913. UpdateLatchedValues( );
  914. return m_latchedEyeDirection;
  915. }
  916. //------------------------------------------------------------------------------
  917. // Purpose : Calculate the NPC's head direction in 2D world space
  918. // Input :
  919. // Output :
  920. //------------------------------------------------------------------------------
  921. Vector CAI_BaseActor::HeadDirection2D( void )
  922. {
  923. Vector vHeadDirection = HeadDirection3D();
  924. vHeadDirection.z = 0;
  925. vHeadDirection.AsVector2D().NormalizeInPlace();
  926. return vHeadDirection;
  927. }
  928. //------------------------------------------------------------------------------
  929. // Purpose : Calculate the NPC's head direction in 3D world space
  930. // Input :
  931. // Output :
  932. //------------------------------------------------------------------------------
  933. Vector CAI_BaseActor::HeadDirection3D( )
  934. {
  935. UpdateLatchedValues( );
  936. return m_latchedHeadDirection;
  937. }
  938. //-----------------------------------------------------------------------------
  939. // Purpose:
  940. //-----------------------------------------------------------------------------
  941. bool CAI_BaseActor::HasActiveLookTargets( void )
  942. {
  943. return m_lookQueue.Count() != 0;
  944. }
  945. //-----------------------------------------------------------------------------
  946. // Purpose: Clear any active look targets for the specified entity
  947. //-----------------------------------------------------------------------------
  948. void CAI_BaseActor::ClearLookTarget( CBaseEntity *pTarget )
  949. {
  950. int iIndex = m_lookQueue.Find( pTarget );
  951. if ( iIndex != m_lookQueue.InvalidIndex() )
  952. {
  953. m_lookQueue.Remove(iIndex);
  954. }
  955. iIndex = m_randomLookQueue.Find( pTarget );
  956. if ( iIndex != m_randomLookQueue.InvalidIndex() )
  957. {
  958. m_randomLookQueue.Remove(iIndex);
  959. // Figure out the new random look time
  960. m_flNextRandomLookTime = gpGlobals->curtime + 1.0;
  961. for (int i = 0; i < m_randomLookQueue.Count(); i++)
  962. {
  963. if ( m_randomLookQueue[i].m_flEndTime > m_flNextRandomLookTime )
  964. {
  965. m_flNextRandomLookTime = m_randomLookQueue[i].m_flEndTime + 0.4;
  966. }
  967. }
  968. }
  969. }
  970. //-----------------------------------------------------------------------------
  971. // Purpose: Look at other NPCs and clients from time to time
  972. //-----------------------------------------------------------------------------
  973. float CAI_BaseActor::PickLookTarget( bool bExcludePlayers, float minTime, float maxTime )
  974. {
  975. return PickLookTarget( m_randomLookQueue, bExcludePlayers, minTime, maxTime );
  976. }
  977. float CAI_BaseActor::PickLookTarget( CAI_InterestTarget &queue, bool bExcludePlayers, float minTime, float maxTime )
  978. {
  979. AILookTargetArgs_t args;
  980. args.vTarget = vec3_invalid;
  981. args.flDuration = random->RandomFloat( minTime, maxTime );
  982. args.flInfluence = random->RandomFloat( 0.3, 0.5 );
  983. args.flRamp = random->RandomFloat( 0.2, 0.4 );
  984. args.bExcludePlayers = bExcludePlayers;
  985. args.pQueue = &queue;
  986. bool foundLookTarget = true;
  987. if ( !PickTacticalLookTarget( &args ) )
  988. {
  989. if ( !PickRandomLookTarget( &args ) )
  990. {
  991. foundLookTarget = false;
  992. }
  993. }
  994. if ( !foundLookTarget )
  995. {
  996. // DevMsg("nothing to see\n" );
  997. MakeRandomLookTarget( &args, minTime, maxTime );
  998. }
  999. // See if derived NPCs want to do anything with this look target before I use it
  1000. OnSelectedLookTarget( &args );
  1001. if ( args.hTarget != NULL )
  1002. {
  1003. Assert( args.vTarget == vec3_invalid );
  1004. queue.Add( args.hTarget, args.flInfluence, args.flDuration, args.flRamp );
  1005. }
  1006. else
  1007. {
  1008. Assert( args.vTarget != vec3_invalid );
  1009. queue.Add( args.vTarget, args.flInfluence, args.flDuration, args.flRamp );
  1010. }
  1011. return args.flDuration;
  1012. }
  1013. bool CAI_BaseActor::PickTacticalLookTarget( AILookTargetArgs_t *pArgs )
  1014. {
  1015. CBaseEntity *pEnemy = GetEnemy();
  1016. if (pEnemy != NULL)
  1017. {
  1018. if ( ( FVisible( pEnemy ) || random->RandomInt(0, 3) == 0 ) && ValidHeadTarget(pEnemy->EyePosition()))
  1019. {
  1020. // look at enemy closer
  1021. pArgs->hTarget = pEnemy;
  1022. pArgs->flInfluence = random->RandomFloat( 0.7, 1.0 );
  1023. pArgs->flRamp = 0;
  1024. return true;
  1025. }
  1026. else
  1027. {
  1028. // look at something else for a shorter time
  1029. pArgs->flDuration = random->RandomFloat( 0.5, 0.8 );
  1030. // move head faster
  1031. pArgs->flRamp = 0.2;
  1032. }
  1033. }
  1034. return false;
  1035. }
  1036. bool CAI_BaseActor::PickRandomLookTarget( AILookTargetArgs_t *pArgs )
  1037. {
  1038. bool bIsNavigating = ( GetNavigator()->IsGoalActive() && GetNavigator()->IsGoalSet() );
  1039. if ( bIsNavigating && random->RandomInt(1, 10) <= 3 )
  1040. {
  1041. Vector navLookPoint;
  1042. Vector delta;
  1043. if ( GetNavigator()->GetPointAlongPath( &navLookPoint, 12 * 12 ) && (delta = navLookPoint - GetAbsOrigin()).Length() > 8.0 * 12.0 )
  1044. {
  1045. if ( random->RandomInt(1, 10) <= 5 )
  1046. {
  1047. pArgs->vTarget = navLookPoint;
  1048. pArgs->flDuration = random->RandomFloat( 0.2, 0.4 );
  1049. }
  1050. else
  1051. {
  1052. pArgs->hTarget = this;
  1053. pArgs->flDuration = random->RandomFloat( 1.0, 2.0 );
  1054. }
  1055. pArgs->flRamp = 0.2;
  1056. return true;
  1057. }
  1058. }
  1059. if ( GetState() == NPC_STATE_COMBAT && random->RandomInt(1, 10) <= 8 )
  1060. {
  1061. // if in combat, look forward 80% of the time?
  1062. pArgs->hTarget = this;
  1063. return true;
  1064. }
  1065. CBaseEntity *pBestEntity = NULL;
  1066. CBaseEntity *pEntity = NULL;
  1067. int iHighestImportance = 0;
  1068. int iConsidered = 0;
  1069. for ( CEntitySphereQuery sphere( GetAbsOrigin(), 30 * 12, 0 ); (pEntity = sphere.GetCurrentEntity()) != NULL; sphere.NextEntity() )
  1070. {
  1071. if (pEntity == this)
  1072. {
  1073. continue;
  1074. }
  1075. if ( pArgs->bExcludePlayers && pEntity->GetFlags() & FL_CLIENT )
  1076. {
  1077. // Don't look at any players.
  1078. continue;
  1079. }
  1080. if (!pEntity->IsViewable())
  1081. {
  1082. // Don't look at things without a model, or aren't tagged as interesting
  1083. continue;
  1084. }
  1085. if ( pEntity->GetOwnerEntity() && !pEntity->GetOwnerEntity()->IsViewable() )
  1086. {
  1087. // Don't look at things that are associated with non-viewable owners.
  1088. // Specifically, this prevents NPC's looking at beams or sprites that
  1089. // are part of a viewmodel. (sjb)
  1090. continue;
  1091. }
  1092. // Don't look at any object that is ultimately parented to the player.
  1093. // These objects will almost always be at the player's origin (feet), and it
  1094. // looks bad when an actor looks at the player's feet. (sjb)
  1095. CBaseEntity *pParent = pEntity->GetParent();
  1096. bool bObjectParentedToPlayer = false;
  1097. while( pParent )
  1098. {
  1099. if( pParent->IsPlayer() )
  1100. {
  1101. bObjectParentedToPlayer = true;
  1102. break;
  1103. }
  1104. pParent = pParent->GetParent();
  1105. }
  1106. if( bObjectParentedToPlayer )
  1107. continue;
  1108. // skip entities we're already looking at
  1109. if ( pArgs->pQueue->Find( pEntity ) != pArgs->pQueue->InvalidIndex() )
  1110. continue;
  1111. // keep track of number of interesting things
  1112. iConsidered++;
  1113. if ((pEntity->GetFlags() & FL_CLIENT) && (pEntity->IsMoving() || random->RandomInt( 0, 2) == 0))
  1114. {
  1115. if (FVisible( pEntity ) && ValidHeadTarget(pEntity->EyePosition()))
  1116. {
  1117. pArgs->flDuration = random->RandomFloat( 1.0, 4.0 );
  1118. pBestEntity = pEntity;
  1119. break;
  1120. }
  1121. }
  1122. Vector delta = (pEntity->EyePosition() - EyePosition());
  1123. VectorNormalize( delta );
  1124. int iImportance;
  1125. #if 0
  1126. // consider things in front to be more important than things to the sides
  1127. iImportance = (DotProduct( delta, HeadDirection3D() );
  1128. #else
  1129. // No, for now, give all targets random priority (as long as they're in front)
  1130. iImportance = random->RandomInt( 1, 100 );
  1131. #endif
  1132. // make other npcs, and moving npc's far more important
  1133. if (pEntity->MyNPCPointer())
  1134. {
  1135. iImportance *= 10;
  1136. if (pEntity->IsMoving())
  1137. {
  1138. iImportance *= 10;
  1139. }
  1140. }
  1141. if ( iImportance > iHighestImportance )
  1142. {
  1143. if (FVisible( pEntity ) && ValidHeadTarget(pEntity->EyePosition()))
  1144. {
  1145. iHighestImportance = iImportance;
  1146. pBestEntity = pEntity;
  1147. // NDebugOverlay::Line( EyePosition(), pEntity->EyePosition(), 0,0,255, false, 0.5);
  1148. }
  1149. }
  1150. }
  1151. // if there were too few things to look at, don't trust the item
  1152. if (iConsidered < random->RandomInt( 0, 5))
  1153. {
  1154. pBestEntity = NULL;
  1155. }
  1156. if (pBestEntity)
  1157. {
  1158. //Msg("looking at %s\n", pBestEntity->GetClassname() );
  1159. //NDebugOverlay::Line( EyePosition(), pBestEntity->WorldSpaceCenter(), 255, 0, 0, false, 5 );
  1160. pArgs->hTarget = pBestEntity;
  1161. return true;
  1162. }
  1163. return false;
  1164. }
  1165. //-----------------------------------------------------------------------------
  1166. // All attempts to find a target have failed, so just make something up.
  1167. //-----------------------------------------------------------------------------
  1168. void CAI_BaseActor::MakeRandomLookTarget( AILookTargetArgs_t *pArgs, float minTime, float maxTime )
  1169. {
  1170. Vector forward, right, up;
  1171. GetVectors( &forward, &right, &up );
  1172. // DevMsg("random view\n");
  1173. // For now, just look farther afield while driving in the vehicle. Without this we look around wildly!
  1174. #ifdef HL2_EPISODIC
  1175. if ( MyCombatCharacterPointer() && MyCombatCharacterPointer()->IsInAVehicle() )
  1176. {
  1177. pArgs->vTarget = EyePosition() + forward * 2048 + right * random->RandomFloat(-650,650) + up * random->RandomFloat(-32,32);
  1178. }
  1179. else
  1180. #endif // HL2_EPISODIC
  1181. {
  1182. pArgs->vTarget = EyePosition() + forward * 128 + right * random->RandomFloat(-32,32) + up * random->RandomFloat(-16,16);
  1183. }
  1184. pArgs->flDuration = random->RandomFloat( minTime, maxTime );
  1185. pArgs->flInfluence = 0.01;
  1186. pArgs->flRamp = random->RandomFloat( 0.8, 2.8 );
  1187. }
  1188. //-----------------------------------------------------------------------------
  1189. // Purpose: Make sure we're looking at what we're shooting at
  1190. //-----------------------------------------------------------------------------
  1191. void CAI_BaseActor::StartTaskRangeAttack1( const Task_t *pTask )
  1192. {
  1193. BaseClass::StartTaskRangeAttack1( pTask );
  1194. if (GetEnemy())
  1195. {
  1196. AddLookTarget( GetEnemy(), 1.0, 0.5, 0.2 );
  1197. }
  1198. }
  1199. //-----------------------------------------------------------------------------
  1200. // Purpose: Set direction that the NPC is looking
  1201. //-----------------------------------------------------------------------------
  1202. void CAI_BaseActor::AddLookTarget( CBaseEntity *pTarget, float flImportance, float flDuration, float flRamp )
  1203. {
  1204. m_lookQueue.Add( pTarget, flImportance, flDuration, flRamp );
  1205. }
  1206. void CAI_BaseActor::AddLookTarget( const Vector &vecPosition, float flImportance, float flDuration, float flRamp )
  1207. {
  1208. m_lookQueue.Add( vecPosition, flImportance, flDuration, flRamp );
  1209. }
  1210. //-----------------------------------------------------------------------------
  1211. // Purpose: Maintain eye, head, body postures, etc.
  1212. //-----------------------------------------------------------------------------
  1213. void CAI_BaseActor::MaintainLookTargets( float flInterval )
  1214. {
  1215. int i;
  1216. if ( m_iszExpressionScene != NULL_STRING && m_hExpressionSceneEnt == NULL )
  1217. {
  1218. InstancedScriptedScene( this, STRING(m_iszExpressionScene), &m_hExpressionSceneEnt, 0.0, true );
  1219. }
  1220. // decay body/spine yaw
  1221. m_goalSpineYaw = m_goalSpineYaw * 0.8;
  1222. m_goalBodyYaw = m_goalBodyYaw * 0.8;
  1223. m_goalHeadCorrection = m_goalHeadCorrection * 0.8;
  1224. // ARRGGHHH, this needs to be moved!!!!
  1225. SetAccumulatedYawAndUpdate( );
  1226. ProcessSceneEvents( );
  1227. MaintainTurnActivity( );
  1228. DoBodyLean( );
  1229. UpdateBodyControl( );
  1230. InvalidateBoneCache();
  1231. // cached versions of the current eye position
  1232. Vector vEyePosition = EyePosition( );
  1233. // FIXME: make this client side and automatic
  1234. // set gesture positions
  1235. Set( m_ParameterGestureHeight, Get( m_FlexweightGestureUpDown ) );
  1236. Set( m_ParameterGestureWidth, Get( m_FlexweightGestureRightLeft ) );
  1237. // initialize goal head direction to be current direction - this frames animation layering/pose parameters -
  1238. // but with the head controlls removed.
  1239. Vector vHead = HeadDirection3D( );
  1240. float flHeadInfluence = 0.0;
  1241. // NDebugOverlay::Line( vEyePosition, vEyePosition + vHead * 16, 0,0,255, false, 0.1);
  1242. // clean up look targets
  1243. m_lookQueue.Cleanup();
  1244. // clean up random look targets
  1245. m_randomLookQueue.Cleanup();
  1246. // clean up synthetic look targets
  1247. m_syntheticLookQueue.Cleanup();
  1248. // if there's real things to look at, turn off the random targets
  1249. if (m_lookQueue.Count() != 0 || m_syntheticLookQueue.Count() != 0)
  1250. {
  1251. for (i = 0; i < m_randomLookQueue.Count(); i++)
  1252. {
  1253. if (gpGlobals->curtime < m_randomLookQueue[i].m_flEndTime - m_randomLookQueue[i].m_flRamp - 0.2)
  1254. {
  1255. m_randomLookQueue[i].m_flEndTime = gpGlobals->curtime + m_randomLookQueue[i].m_flRamp + 0.2;
  1256. }
  1257. }
  1258. m_flNextRandomLookTime = gpGlobals->curtime + 1.0;
  1259. }
  1260. else if (gpGlobals->curtime >= m_flNextRandomLookTime && GetState() != NPC_STATE_SCRIPT)
  1261. {
  1262. // Look at whatever!
  1263. m_flNextRandomLookTime = gpGlobals->curtime + PickLookTarget( m_randomLookQueue ) - 0.4;
  1264. }
  1265. // don't bother with any of the rest if the player can't see you
  1266. if (!HasCondition( COND_IN_PVS ))
  1267. {
  1268. return;
  1269. }
  1270. // Time to finish the current random expression? Or time to pick a new one?
  1271. if ( m_flNextRandomExpressionTime && gpGlobals->curtime > m_flNextRandomExpressionTime )
  1272. {
  1273. // Random expressions need to be cleared, because they don't loop. So if we
  1274. // pick the same one again, we want to restart it.
  1275. ClearExpression();
  1276. PlayExpressionForState( GetState() );
  1277. }
  1278. CUtlVector<CAI_InterestTarget_t *> active;
  1279. // clean up random look targets
  1280. for (i = 0; i < m_randomLookQueue.Count(); i++)
  1281. {
  1282. active.AddToTail( &m_randomLookQueue[i] );
  1283. }
  1284. for (i = 0; i < m_lookQueue.Count(); i++)
  1285. {
  1286. active.AddToTail( &m_lookQueue[i] );
  1287. }
  1288. for (i = 0; i < m_syntheticLookQueue.Count(); i++)
  1289. {
  1290. active.AddToTail( &m_syntheticLookQueue[i] );
  1291. }
  1292. // figure out ideal head yaw
  1293. bool bValidHeadTarget = false;
  1294. bool bExpectedHeadTarget = false;
  1295. for (i = 0; i < active.Count();i++)
  1296. {
  1297. Vector dir;
  1298. float flDist = 100.0f;
  1299. bExpectedHeadTarget = true;
  1300. float flInterest = active[i]->Interest( );
  1301. if (active[i]->IsThis( this ))
  1302. {
  1303. int iForward = LookupAttachment( "forward" );
  1304. if ( iForward > 0)
  1305. {
  1306. Vector tmp1;
  1307. GetAttachment( iForward, tmp1, &dir, NULL, NULL );
  1308. }
  1309. else
  1310. {
  1311. dir = HeadDirection3D();
  1312. }
  1313. }
  1314. else
  1315. {
  1316. dir = active[i]->GetPosition() - vEyePosition;
  1317. flDist = VectorNormalize( dir );
  1318. flInterest = flInterest * HeadTargetValidity( active[i]->GetPosition() );
  1319. }
  1320. /*
  1321. if ( (m_debugOverlays & OVERLAY_NPC_SELECTED_BIT) )
  1322. {
  1323. DevMsg( "head (%d) %.2f : %s : %.1f %.1f %.1f\n", i, flInterest, active[i]->m_hTarget->GetClassname(), active[i]->GetPosition().x, active[i]->GetPosition().y, active[i]->GetPosition().z );
  1324. }
  1325. */
  1326. if (flInterest > 0.0)
  1327. {
  1328. if (flHeadInfluence == 0.0)
  1329. {
  1330. vHead = dir;
  1331. flHeadInfluence = flInterest;
  1332. }
  1333. else
  1334. {
  1335. flHeadInfluence = flHeadInfluence * (1 - flInterest) + flInterest;
  1336. float w = flInterest / flHeadInfluence;
  1337. vHead = vHead * (1 - w) + dir * w;
  1338. }
  1339. bValidHeadTarget = true;
  1340. // NDebugOverlay::Line( vEyePosition, vEyePosition + dir * 64, 0,255,0, false, 0.1);
  1341. }
  1342. else
  1343. {
  1344. // NDebugOverlay::Line( vEyePosition, active[i]->GetPosition(), 255,0,0, false, 0.1);
  1345. }
  1346. }
  1347. Assert( flHeadInfluence <= 1.0 );
  1348. // turn head toward target
  1349. if (bValidHeadTarget)
  1350. {
  1351. UpdateHeadControl( vEyePosition + vHead * 100, flHeadInfluence );
  1352. m_goalHeadDirection = vHead;
  1353. m_goalHeadInfluence = flHeadInfluence;
  1354. }
  1355. else
  1356. {
  1357. // no target, decay all head control direction
  1358. m_goalHeadDirection = m_goalHeadDirection * 0.8 + vHead * 0.2;
  1359. m_goalHeadInfluence = MAX( m_goalHeadInfluence - 0.2, 0 );
  1360. VectorNormalize( m_goalHeadDirection );
  1361. UpdateHeadControl( vEyePosition + m_goalHeadDirection * 100, m_goalHeadInfluence );
  1362. // NDebugOverlay::Line( vEyePosition, vEyePosition + m_goalHeadDirection * 100, 255,0,0, false, 0.1);
  1363. }
  1364. // DevMsg( "%.1f %.1f ", GetPoseParameter( "head_pitch" ), GetPoseParameter( "head_roll" ) );
  1365. // figure out eye target
  1366. // eyes need to look directly at a target, even if the head doesn't quite aim there yet.
  1367. bool bFoundTarget = false;
  1368. EHANDLE hTarget = NULL;
  1369. for (i = active.Count() - 1; i >= 0; i--)
  1370. {
  1371. if (active[i]->IsThis( this ))
  1372. {
  1373. // DevMsg( "eyes (%d) %s\n", i, STRING( active[i]->m_hTarget->GetEntityName().ToCStr() ) );
  1374. bFoundTarget = true;
  1375. hTarget = this;
  1376. SetViewtarget( vEyePosition + HeadDirection3D() * 100 );
  1377. // NDebugOverlay::Line( vEyePosition, vEyePosition + HeadDirection3D() * 100, 255,0,0, false, 0.1);
  1378. break;
  1379. }
  1380. else
  1381. {
  1382. // E3 Hack
  1383. if (ValidEyeTarget(active[i]->GetPosition()))
  1384. {
  1385. // DevMsg( "eyes (%d) %s\n", i, STRING( pTarget->GetEntityName().ToCStr() ) );
  1386. bFoundTarget = true;
  1387. hTarget = active[i]->m_hTarget;
  1388. SetViewtarget( active[i]->GetPosition() );
  1389. break;
  1390. }
  1391. }
  1392. }
  1393. // FIXME: add blink when changing targets
  1394. if (m_hLookTarget != hTarget)
  1395. {
  1396. m_flBlinktime -= 0.5;
  1397. m_hLookTarget = hTarget;
  1398. if ( (m_debugOverlays & OVERLAY_NPC_SELECTED_BIT) && ai_debug_looktargets.GetInt() == 2 && m_hLookTarget.Get() )
  1399. {
  1400. if ( m_hLookTarget != this )
  1401. {
  1402. Vector vecEyePos = m_hLookTarget->EyePosition();
  1403. NDebugOverlay::Box( vecEyePos, -Vector(5,5,5), Vector(5,5,5), 0, 255, 0, 255, 20 );
  1404. NDebugOverlay::Line( EyePosition(), vecEyePos, 0,255,0, true, 20 );
  1405. NDebugOverlay::Text( vecEyePos, UTIL_VarArgs( "%s (%s)", m_hLookTarget->GetClassname(), m_hLookTarget->GetDebugName() ), false, 20 );
  1406. }
  1407. }
  1408. OnNewLookTarget();
  1409. }
  1410. // this should take into acount where it will try to be....
  1411. if (!bFoundTarget && !ValidEyeTarget( GetViewtarget() ))
  1412. {
  1413. Vector right, up;
  1414. VectorVectors( HeadDirection3D(), right, up );
  1415. // DevMsg("random view\n");
  1416. SetViewtarget( EyePosition() + HeadDirection3D() * 128 + right * random->RandomFloat(-32,32) + up * random->RandomFloat(-16,16) );
  1417. }
  1418. if ( m_hLookTarget != NULL )
  1419. {
  1420. Vector absVel = m_hLookTarget->GetAbsVelocity();
  1421. CBaseEntity *ground = m_hLookTarget->GetGroundEntity();
  1422. if ( ground && ground->GetMoveType() == MOVETYPE_PUSH)
  1423. {
  1424. absVel = absVel + ground->GetAbsVelocity();
  1425. }
  1426. #ifdef HL2_EPISODIC
  1427. // Translate our position if riding in a vehicle
  1428. if ( m_hLookTarget->MyCombatCharacterPointer() )
  1429. {
  1430. CBaseCombatCharacter *pBCC = m_hLookTarget->MyCombatCharacterPointer();
  1431. CBaseEntity *pVehicle = pBCC->GetVehicleEntity();
  1432. if ( pVehicle )
  1433. {
  1434. IPhysicsObject *pObj = pVehicle->VPhysicsGetObject();
  1435. if ( pObj )
  1436. {
  1437. Vector vecVelocity;
  1438. pObj->GetVelocity( &vecVelocity, NULL );
  1439. absVel += vecVelocity;
  1440. }
  1441. }
  1442. }
  1443. #endif //HL2_EPISODIC
  1444. if ( !VectorCompare( absVel, vec3_origin ) )
  1445. {
  1446. Vector viewTarget = GetViewtarget();
  1447. // Forward one think cycle
  1448. viewTarget += absVel * flInterval;
  1449. SetViewtarget( viewTarget );
  1450. }
  1451. }
  1452. // NDebugOverlay::Triangle( vEyePosition, GetViewtarget(), GetAbsOrigin(), 255, 255, 255, 10, false, 0.1 );
  1453. // DevMsg("pitch %.1f yaw %.1f\n", GetFlexWeight( "eyes_updown" ), GetFlexWeight( "eyes_rightleft" ) );
  1454. // blink
  1455. if (m_flBlinktime < gpGlobals->curtime)
  1456. {
  1457. Blink();
  1458. m_flBlinktime = gpGlobals->curtime + random->RandomFloat( 1.5, 4.5 );
  1459. }
  1460. if ( ai_debug_looktargets.GetInt() == 1 && (m_debugOverlays & OVERLAY_NPC_SELECTED_BIT) )
  1461. {
  1462. NDebugOverlay::Box( GetViewtarget(), -Vector(2,2,2), Vector(2,2,2), 0, 255, 0, 0, 20 );
  1463. NDebugOverlay::Line( EyePosition(),GetViewtarget(), 0,255,0, false, .1 );
  1464. }
  1465. }
  1466. //-----------------------------------------------------------------------------
  1467. void CAI_BaseActor::PlayExpressionForState( NPC_STATE state )
  1468. {
  1469. // If we have an override expression, use it above everything else
  1470. if ( m_iszExpressionOverride != NULL_STRING && state != NPC_STATE_DEAD )
  1471. {
  1472. SetExpression( STRING(m_iszExpressionOverride) );
  1473. return;
  1474. }
  1475. // If we have a random expression, use that
  1476. const char *pszExpression = SelectRandomExpressionForState( state );
  1477. if ( pszExpression && *pszExpression )
  1478. {
  1479. float flDuration = SetExpression( pszExpression );
  1480. m_flNextRandomExpressionTime = gpGlobals->curtime + flDuration;
  1481. return;
  1482. }
  1483. else
  1484. {
  1485. // Stop looking for random expressions for this state
  1486. m_flNextRandomExpressionTime = 0;
  1487. }
  1488. // Lastly, use the base expression loops
  1489. switch ( state )
  1490. {
  1491. case NPC_STATE_IDLE:
  1492. if ( m_iszIdleExpression != NULL_STRING )
  1493. {
  1494. SetExpression( STRING(m_iszIdleExpression) );
  1495. }
  1496. break;
  1497. case NPC_STATE_COMBAT:
  1498. if ( m_iszCombatExpression != NULL_STRING )
  1499. {
  1500. SetExpression( STRING(m_iszCombatExpression) );
  1501. }
  1502. break;
  1503. case NPC_STATE_ALERT:
  1504. if ( m_iszAlertExpression != NULL_STRING )
  1505. {
  1506. SetExpression( STRING(m_iszAlertExpression) );
  1507. }
  1508. break;
  1509. case NPC_STATE_PLAYDEAD:
  1510. case NPC_STATE_DEAD:
  1511. if ( m_iszDeathExpression != NULL_STRING )
  1512. {
  1513. SetExpression( STRING(m_iszDeathExpression) );
  1514. }
  1515. break;
  1516. }
  1517. }
  1518. //-----------------------------------------------------------------------------
  1519. // Purpose: Return a random expression for the specified state to play over
  1520. // the state's expression loop.
  1521. //-----------------------------------------------------------------------------
  1522. const char *CAI_BaseActor::SelectRandomExpressionForState( NPC_STATE state )
  1523. {
  1524. return NULL;
  1525. }
  1526. //-----------------------------------------------------------------------------
  1527. void CAI_BaseActor::OnStateChange( NPC_STATE OldState, NPC_STATE NewState )
  1528. {
  1529. PlayExpressionForState( NewState );
  1530. #ifdef HL2_EPISODIC
  1531. // If we've just switched states, ensure we stop any scenes that asked to be stopped
  1532. if ( OldState == NPC_STATE_IDLE )
  1533. {
  1534. RemoveActorFromScriptedScenes( this, true, true );
  1535. }
  1536. #endif
  1537. BaseClass::OnStateChange( OldState, NewState );
  1538. }
  1539. //-----------------------------------------------------------------------------
  1540. float CAI_BaseActor::SetExpression( const char *pszExpressionScene )
  1541. {
  1542. if ( !pszExpressionScene || !*pszExpressionScene )
  1543. {
  1544. ClearExpression();
  1545. return 0;
  1546. }
  1547. if ( m_iszExpressionScene != NULL_STRING && stricmp( STRING(m_iszExpressionScene), pszExpressionScene ) == 0 )
  1548. {
  1549. return 0;
  1550. }
  1551. if ( m_hExpressionSceneEnt != NULL )
  1552. {
  1553. StopScriptedScene( this, m_hExpressionSceneEnt );
  1554. }
  1555. if ( ai_debug_expressions.GetInt() )
  1556. {
  1557. Msg("%s (%s) set expression to: %s\n", GetClassname(), GetDebugName(), pszExpressionScene );
  1558. }
  1559. m_iszExpressionScene = NULL_STRING;
  1560. if ( pszExpressionScene )
  1561. {
  1562. float flDuration = InstancedScriptedScene( this, pszExpressionScene, &m_hExpressionSceneEnt, 0.0, true );
  1563. if ( m_hExpressionSceneEnt != NULL )
  1564. {
  1565. m_iszExpressionScene = AllocPooledString( pszExpressionScene );
  1566. }
  1567. return flDuration;
  1568. }
  1569. return 0;
  1570. }
  1571. //-----------------------------------------------------------------------------
  1572. void CAI_BaseActor::ClearExpression()
  1573. {
  1574. if ( m_hExpressionSceneEnt != NULL )
  1575. {
  1576. StopScriptedScene( this, m_hExpressionSceneEnt );
  1577. }
  1578. m_iszExpressionScene = NULL_STRING;
  1579. }
  1580. //-----------------------------------------------------------------------------
  1581. const char *CAI_BaseActor::GetExpression()
  1582. {
  1583. return STRING(m_iszExpressionScene);
  1584. }
  1585. //-----------------------------------------------------------------------------
  1586. void CAI_BaseActor::InputSetExpressionOverride( inputdata_t &inputdata )
  1587. {
  1588. bool fHadOverride = ( m_iszExpressionOverride != NULL_STRING );
  1589. m_iszExpressionOverride = inputdata.value.StringID();
  1590. if ( m_iszExpressionOverride != NULL_STRING )
  1591. {
  1592. SetExpression( STRING(m_iszExpressionOverride) );
  1593. }
  1594. else if ( fHadOverride )
  1595. {
  1596. PlayExpressionForState( GetState() );
  1597. }
  1598. }
  1599. //-----------------------------------------------------------------------------
  1600. bool CAI_BaseActor::UseSemaphore( void )
  1601. {
  1602. if ( m_bDontUseSemaphore )
  1603. return false;
  1604. return true;
  1605. }
  1606. //-----------------------------------------------------------------------------
  1607. CAI_Expresser *CAI_BaseActor::CreateExpresser()
  1608. {
  1609. m_pExpresser = new CAI_Expresser(this);
  1610. return m_pExpresser;
  1611. }
  1612. //-----------------------------------------------------------------------------
  1613. CAI_Expresser *CAI_BaseActor::GetExpresser()
  1614. {
  1615. return m_pExpresser;
  1616. }
  1617. //-----------------------------------------------------------------------------
  1618. bool CAI_BaseActor::CreateComponents()
  1619. {
  1620. if ( !BaseClass::CreateComponents() )
  1621. return false;
  1622. m_pExpresser = CreateExpresser();
  1623. if ( !m_pExpresser)
  1624. return false;
  1625. m_pExpresser->Connect(this);
  1626. return true;
  1627. }
  1628. //-----------------------------------------------------------------------------