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.

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