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.

2887 lines
81 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "animation.h"
  8. #include "baseflex.h"
  9. #include "filesystem.h"
  10. #include "studio.h"
  11. #include "choreoevent.h"
  12. #include "choreoscene.h"
  13. #include "choreoactor.h"
  14. #include "vstdlib/random.h"
  15. #include "engine/IEngineSound.h"
  16. #include "tier1/strtools.h"
  17. #include "keyvalues.h"
  18. #include "ai_basenpc.h"
  19. #include "ai_navigator.h"
  20. #include "ai_moveprobe.h"
  21. #include "sceneentity.h"
  22. #include "ai_baseactor.h"
  23. #include "datacache/imdlcache.h"
  24. #include "tier1/byteswap.h"
  25. #include "toolframework/itoolframework.h"
  26. #include "flexcycler.h"
  27. // memdbgon must be the last include file in a .cpp file!!!
  28. #include "tier0/memdbgon.h"
  29. static ConVar scene_showlook( "scene_showlook", "0", FCVAR_ARCHIVE, "When playing back, show the directions of look events." );
  30. static ConVar scene_showmoveto( "scene_showmoveto", "0", FCVAR_ARCHIVE, "When moving, show the end location." );
  31. static ConVar scene_showunlock( "scene_showunlock", "0", FCVAR_ARCHIVE, "Show when a vcd is playing but normal AI is running." );
  32. // static ConVar scene_checktagposition( "scene_checktagposition", "0", FCVAR_ARCHIVE, "When playing back a choreographed scene, check the current position of the tags relative to where they were authored." );
  33. // Fake layer # to force HandleProcessSceneEvent to actually allocate the layer during npc think time instead of in between.
  34. #define REQUEST_DEFERRED_LAYER_ALLOCATION -2
  35. extern bool g_bClientFlex;
  36. // ---------------------------------------------------------------------
  37. //
  38. // CBaseFlex -- physically simulated brush rectangular solid
  39. //
  40. // ---------------------------------------------------------------------
  41. void* SendProxy_FlexWeights( const SendProp *pProp, const void *pStruct, const void *pVarData, CSendProxyRecipients *pRecipients, int objectID )
  42. {
  43. // Don't any flexweights to client unless scene_clientflex.GetBool() is false
  44. if ( !g_bClientFlex )
  45. return (void*)pVarData;
  46. else
  47. return NULL;
  48. }
  49. REGISTER_SEND_PROXY_NON_MODIFIED_POINTER( SendProxy_FlexWeights );
  50. // SendTable stuff.
  51. IMPLEMENT_SERVERCLASS_ST(CBaseFlex, DT_BaseFlex)
  52. // Note we can't totally disabled flexweights transmission since some things like blink and eye tracking are still done by the server
  53. SendPropArray3 (SENDINFO_ARRAY3(m_flexWeight), SendPropFloat(SENDINFO_ARRAY(m_flexWeight), 12, SPROP_ROUNDDOWN, 0.0f, 1.0f ) /*, SendProxy_FlexWeights*/ ),
  54. SendPropInt (SENDINFO(m_blinktoggle), 1, SPROP_UNSIGNED ),
  55. SendPropVector (SENDINFO(m_viewtarget), -1, SPROP_COORD),
  56. #ifdef HL2_DLL
  57. SendPropFloat ( SENDINFO_VECTORELEM(m_vecViewOffset, 0), 0, SPROP_NOSCALE ),
  58. SendPropFloat ( SENDINFO_VECTORELEM(m_vecViewOffset, 1), 0, SPROP_NOSCALE ),
  59. SendPropFloat ( SENDINFO_VECTORELEM(m_vecViewOffset, 2), 0, SPROP_NOSCALE ),
  60. SendPropVector ( SENDINFO(m_vecLean), -1, SPROP_COORD ),
  61. SendPropVector ( SENDINFO(m_vecShift), -1, SPROP_COORD ),
  62. #endif
  63. END_SEND_TABLE()
  64. BEGIN_DATADESC( CBaseFlex )
  65. // m_blinktoggle
  66. DEFINE_ARRAY( m_flexWeight, FIELD_FLOAT, MAXSTUDIOFLEXCTRL ),
  67. DEFINE_FIELD( m_viewtarget, FIELD_POSITION_VECTOR ),
  68. // m_SceneEvents
  69. // m_FileList
  70. DEFINE_FIELD( m_flAllowResponsesEndTime, FIELD_TIME ),
  71. // m_ActiveChoreoScenes
  72. // DEFINE_FIELD( m_LocalToGlobal, CUtlRBTree < FS_LocalToGlobal_t , unsigned short > ),
  73. // m_bUpdateLayerPriorities
  74. DEFINE_FIELD( m_flLastFlexAnimationTime, FIELD_TIME ),
  75. #ifdef HL2_DLL
  76. //DEFINE_FIELD( m_vecPrevOrigin, FIELD_POSITION_VECTOR ),
  77. //DEFINE_FIELD( m_vecPrevVelocity, FIELD_VECTOR ),
  78. DEFINE_FIELD( m_vecLean, FIELD_VECTOR ),
  79. DEFINE_FIELD( m_vecShift, FIELD_VECTOR ),
  80. #endif
  81. END_DATADESC()
  82. BEGIN_ENT_SCRIPTDESC( CBaseFlex, CBaseAnimating, "Animated characters who have vertex flex capability." )
  83. DEFINE_SCRIPTFUNC_NAMED( ScriptGetOldestScene, "GetCurrentScene", "Returns the instance of the oldest active scene entity (if any)." )
  84. DEFINE_SCRIPTFUNC_NAMED( ScriptGetSceneByIndex, "GetSceneByIndex", "Returns the instance of the scene entity at the specified index." )
  85. END_SCRIPTDESC();
  86. LINK_ENTITY_TO_CLASS( funCBaseFlex, CBaseFlex ); // meaningless independant class!!
  87. CBaseFlex::CBaseFlex( void ) :
  88. m_LocalToGlobal( 0, 0, FlexSettingLessFunc )
  89. {
  90. #ifdef _DEBUG
  91. // default constructor sets the viewtarget to NAN
  92. m_viewtarget.Init();
  93. #endif
  94. m_bUpdateLayerPriorities = true;
  95. m_flLastFlexAnimationTime = 0.0;
  96. }
  97. CBaseFlex::~CBaseFlex( void )
  98. {
  99. m_LocalToGlobal.RemoveAll();
  100. // If a player is forcibly removed while speaking a response .vcd we can get into here w/ some events waiting to
  101. // be closed out. It's benign and we just discard the events. The CSceneEntity uses EHANDLEs so it'll wipe the
  102. // scene out on next think anyway.
  103. m_SceneEvents.RemoveAll();
  104. }
  105. void CBaseFlex::SetModel( const char *szModelName )
  106. {
  107. MDLCACHE_CRITICAL_SECTION();
  108. BaseClass::SetModel( szModelName );
  109. for (LocalFlexController_t i = LocalFlexController_t(0); i < GetNumFlexControllers(); i++)
  110. {
  111. SetFlexWeight( i, 0.0f );
  112. }
  113. }
  114. CStudioHdr* CBaseFlex::OnNewModel()
  115. {
  116. return BaseClass::OnNewModel();
  117. }
  118. void CBaseFlex::SetViewtarget( const Vector &viewtarget )
  119. {
  120. m_viewtarget = viewtarget; // bah
  121. }
  122. void CBaseFlex::SetFlexWeight( LocalFlexController_t index, float value )
  123. {
  124. if (index >= 0 && index < GetNumFlexControllers())
  125. {
  126. CStudioHdr *pstudiohdr = GetModelPtr( );
  127. if (! pstudiohdr)
  128. return;
  129. mstudioflexcontroller_t *pflexcontroller = pstudiohdr->pFlexcontroller( index );
  130. if (pflexcontroller->max != pflexcontroller->min)
  131. {
  132. value = (value - pflexcontroller->min) / (pflexcontroller->max - pflexcontroller->min);
  133. value = clamp( value, 0.0, 1.0 );
  134. }
  135. m_flexWeight.Set( index, value );
  136. }
  137. }
  138. float CBaseFlex::GetFlexWeight( LocalFlexController_t index )
  139. {
  140. if (index >= 0 && index < GetNumFlexControllers())
  141. {
  142. CStudioHdr *pstudiohdr = GetModelPtr( );
  143. if (! pstudiohdr)
  144. return 0;
  145. mstudioflexcontroller_t *pflexcontroller = pstudiohdr->pFlexcontroller( index );
  146. if (pflexcontroller->max != pflexcontroller->min)
  147. {
  148. return m_flexWeight[index] * (pflexcontroller->max - pflexcontroller->min) + pflexcontroller->min;
  149. }
  150. return m_flexWeight[index];
  151. }
  152. return 0.0;
  153. }
  154. LocalFlexController_t CBaseFlex::FindFlexController( const char *szName )
  155. {
  156. for (LocalFlexController_t i = LocalFlexController_t(0); i < GetNumFlexControllers(); i++)
  157. {
  158. if (stricmp( GetFlexControllerName( i ), szName ) == 0)
  159. {
  160. return i;
  161. }
  162. }
  163. // AssertMsg( 0, UTIL_VarArgs( "flexcontroller %s couldn't be mapped!!!\n", szName ) );
  164. return LocalFlexController_t(0);
  165. }
  166. //-----------------------------------------------------------------------------
  167. // Purpose:
  168. //-----------------------------------------------------------------------------
  169. void CBaseFlex::StartChoreoScene( CChoreoScene *scene )
  170. {
  171. if ( m_ActiveChoreoScenes.Find( scene ) != m_ActiveChoreoScenes.InvalidIndex() )
  172. {
  173. return;
  174. }
  175. m_ActiveChoreoScenes.AddToTail( scene );
  176. m_bUpdateLayerPriorities = true;
  177. }
  178. //-----------------------------------------------------------------------------
  179. // Purpose:
  180. //-----------------------------------------------------------------------------
  181. void CBaseFlex::RemoveChoreoScene( CChoreoScene *scene, bool canceled )
  182. {
  183. // Assert( m_ActiveChoreoScenes.Find( scene ) != m_ActiveChoreoScenes.InvalidIndex() );
  184. m_ActiveChoreoScenes.FindAndRemove( scene );
  185. m_bUpdateLayerPriorities = true;
  186. if (canceled)
  187. {
  188. CAI_BaseNPC *myNpc = MyNPCPointer( );
  189. if ( myNpc )
  190. {
  191. myNpc->ClearSceneLock( );
  192. }
  193. }
  194. }
  195. //-----------------------------------------------------------------------------
  196. // Purpose:
  197. //-----------------------------------------------------------------------------
  198. int CBaseFlex::GetScenePriority( CChoreoScene *scene )
  199. {
  200. int iPriority = 0;
  201. int c = m_ActiveChoreoScenes.Count();
  202. // count number of channels in scenes older than current
  203. for ( int i = 0; i < c; i++ )
  204. {
  205. CChoreoScene *pScene = m_ActiveChoreoScenes[ i ];
  206. if ( !pScene )
  207. {
  208. continue;
  209. }
  210. if ( pScene == scene )
  211. {
  212. break;
  213. }
  214. iPriority += pScene->GetNumChannels( );
  215. }
  216. return iPriority;
  217. }
  218. //-----------------------------------------------------------------------------
  219. // Purpose: Remove all active SceneEvents
  220. //-----------------------------------------------------------------------------
  221. void CBaseFlex::ClearSceneEvents( CChoreoScene *scene, bool canceled )
  222. {
  223. if ( !scene )
  224. {
  225. m_SceneEvents.RemoveAll();
  226. return;
  227. }
  228. for ( int i = m_SceneEvents.Count() - 1; i >= 0; i-- )
  229. {
  230. CSceneEventInfo *info = &m_SceneEvents[ i ];
  231. Assert( info );
  232. Assert( info->m_pScene );
  233. Assert( info->m_pEvent );
  234. if ( info->m_pScene != scene )
  235. continue;
  236. if ( !ClearSceneEvent( info, false, canceled ))
  237. {
  238. // unknown expression to clear!!
  239. Assert( 0 );
  240. }
  241. // Free this slot
  242. info->m_pEvent = NULL;
  243. info->m_pScene = NULL;
  244. info->m_bStarted = false;
  245. m_SceneEvents.Remove( i );
  246. }
  247. }
  248. //-----------------------------------------------------------------------------
  249. // Purpose: Stop specifics of expression
  250. //-----------------------------------------------------------------------------
  251. bool CBaseFlex::ClearSceneEvent( CSceneEventInfo *info, bool fastKill, bool canceled )
  252. {
  253. Assert( info );
  254. Assert( info->m_pScene );
  255. Assert( info->m_pEvent );
  256. // FIXME: this code looks duplicated
  257. switch ( info->m_pEvent->GetType() )
  258. {
  259. case CChoreoEvent::GESTURE:
  260. case CChoreoEvent::SEQUENCE:
  261. {
  262. if (info->m_iLayer >= 0)
  263. {
  264. if ( fastKill )
  265. {
  266. FastRemoveLayer( info->m_iLayer );
  267. }
  268. else if (info->m_pEvent->GetType() == CChoreoEvent::GESTURE)
  269. {
  270. if (canceled)
  271. {
  272. // remove slower if interrupted
  273. RemoveLayer( info->m_iLayer, 0.5 );
  274. }
  275. else
  276. {
  277. RemoveLayer( info->m_iLayer, 0.1 );
  278. }
  279. }
  280. else
  281. {
  282. RemoveLayer( info->m_iLayer, 0.3 );
  283. }
  284. }
  285. }
  286. return true;
  287. case CChoreoEvent::MOVETO:
  288. {
  289. CAI_BaseNPC *myNpc = MyNPCPointer( );
  290. if (!myNpc)
  291. return true;
  292. // cancel moveto if it's distance based, of if the event was part of a canceled vcd
  293. if (IsMoving() && (canceled || info->m_pEvent->GetDistanceToTarget() > 0.0))
  294. {
  295. if (!info->m_bHasArrived)
  296. {
  297. if (info->m_pScene)
  298. {
  299. Scene_Printf( "%s : %8.2f: MOVETO canceled but actor %s not at goal\n", info->m_pScene->GetFilename(), info->m_pScene->GetTime(), info->m_pEvent->GetActor()->GetName() );
  300. }
  301. }
  302. myNpc->GetNavigator()->StopMoving( false ); // Stop moving
  303. }
  304. }
  305. return true;
  306. case CChoreoEvent::FACE:
  307. case CChoreoEvent::FLEXANIMATION:
  308. case CChoreoEvent::EXPRESSION:
  309. case CChoreoEvent::LOOKAT:
  310. case CChoreoEvent::GENERIC:
  311. case CChoreoEvent::CAMERA:
  312. case CChoreoEvent::SCRIPT:
  313. {
  314. // no special rules
  315. }
  316. return true;
  317. case CChoreoEvent::SPEAK:
  318. {
  319. // Tracker 15420: Issue stopsound if we need to cut this short...
  320. if ( canceled )
  321. {
  322. StopSound( info->m_pEvent->GetParameters() );
  323. #ifdef HL2_EPISODIC
  324. // If we were holding the semaphore because of this speech, release it
  325. CAI_BaseActor *pBaseActor = dynamic_cast<CAI_BaseActor*>(this);
  326. if ( pBaseActor )
  327. {
  328. pBaseActor->GetExpresser()->ForceNotSpeaking();
  329. }
  330. #endif
  331. }
  332. }
  333. return true;
  334. }
  335. return false;
  336. }
  337. //-----------------------------------------------------------------------------
  338. // Purpose: Add string indexed scene/expression/duration to list of active SceneEvents
  339. // Input : scenefile -
  340. // expression -
  341. // duration -
  342. //-----------------------------------------------------------------------------
  343. void CBaseFlex::AddSceneEvent( CChoreoScene *scene, CChoreoEvent *event, CBaseEntity *pTarget /*= NULL*/, CSceneEntity* pSceneEntity /*= NULL*/ )
  344. {
  345. if ( !scene || !event )
  346. {
  347. Msg( "CBaseFlex::AddSceneEvent: scene or event was NULL!!!\n" );
  348. return;
  349. }
  350. CChoreoActor *actor = event->GetActor();
  351. if ( !actor )
  352. {
  353. Msg( "CBaseFlex::AddSceneEvent: event->GetActor() was NULL!!!\n" );
  354. return;
  355. }
  356. CSceneEventInfo info;
  357. memset( (void *)&info, 0, sizeof( info ) );
  358. info.m_pEvent = event;
  359. info.m_pScene = scene;
  360. info.m_hTarget = pTarget;
  361. info.m_bStarted = false;
  362. info.m_hSceneEntity = pSceneEntity;
  363. if (StartSceneEvent( &info, scene, event, actor, pTarget ))
  364. {
  365. m_SceneEvents.AddToTail( info );
  366. }
  367. else
  368. {
  369. Scene_Printf( "CBaseFlex::AddSceneEvent: event failed\n" );
  370. // Assert( 0 ); // expression failed to start
  371. }
  372. }
  373. //-----------------------------------------------------------------------------
  374. // Starting various expression types
  375. //-----------------------------------------------------------------------------
  376. bool CBaseFlex::RequestStartSequenceSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event, CChoreoActor *actor, CBaseEntity *pTarget )
  377. {
  378. info->m_nSequence = LookupSequence( event->GetParameters() );
  379. // make sure sequence exists
  380. if (info->m_nSequence < 0)
  381. {
  382. Warning( "CSceneEntity %s :\"%s\" unable to find sequence \"%s\"\n", STRING(GetEntityName()), actor->GetName(), event->GetParameters() );
  383. return false;
  384. }
  385. // This is a bit of a hack, but we need to defer the actual allocation until Process which will sync the layer allocation
  386. // to the NPCs think/m_flAnimTime instead of some arbitrary tick
  387. info->m_iLayer = REQUEST_DEFERRED_LAYER_ALLOCATION;
  388. info->m_pActor = actor;
  389. return true;
  390. }
  391. bool CBaseFlex::RequestStartGestureSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event, CChoreoActor *actor, CBaseEntity *pTarget )
  392. {
  393. info->m_nSequence = LookupSequence( event->GetParameters() );
  394. // make sure sequence exists
  395. if (info->m_nSequence < 0)
  396. {
  397. Warning( "CSceneEntity %s :\"%s\" unable to find gesture \"%s\"\n", STRING(GetEntityName()), actor->GetName(), event->GetParameters() );
  398. return false;
  399. }
  400. // This is a bit of a hack, but we need to defer the actual allocation until Process which will sync the layer allocation
  401. // to the NPCs think/m_flAnimTime instead of some arbitrary tick
  402. info->m_iLayer = REQUEST_DEFERRED_LAYER_ALLOCATION;
  403. info->m_pActor = actor;
  404. return true;
  405. }
  406. bool CBaseFlex::HandleStartSequenceSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event, CChoreoActor *actor )
  407. {
  408. Assert( info->m_iLayer == REQUEST_DEFERRED_LAYER_ALLOCATION );
  409. info->m_nSequence = LookupSequence( event->GetParameters() );
  410. info->m_iLayer = -1;
  411. if (info->m_nSequence < 0)
  412. {
  413. Warning( "CSceneEntity %s :\"%s\" unable to find sequence \"%s\"\n", STRING(GetEntityName()), actor->GetName(), event->GetParameters() );
  414. return false;
  415. }
  416. if (!EnterSceneSequence( scene, event ))
  417. {
  418. if (!event->GetPlayOverScript())
  419. {
  420. // this has failed to start
  421. Warning( "CSceneEntity %s :\"%s\" failed to start sequence \"%s\"\n", STRING(GetEntityName()), actor->GetName(), event->GetParameters() );
  422. return false;
  423. }
  424. // Start anyways, just use normal no-movement, must be in IDLE rules
  425. }
  426. info->m_iPriority = actor->FindChannelIndex( event->GetChannel() );
  427. info->m_iLayer = AddLayeredSequence( info->m_nSequence, info->m_iPriority + GetScenePriority( scene ) );
  428. SetLayerNoRestore( info->m_iLayer, true );
  429. SetLayerWeight( info->m_iLayer, 0.0 );
  430. bool looping = ((GetSequenceFlags( GetModelPtr(), info->m_nSequence ) & STUDIO_LOOPING) != 0);
  431. if (!looping)
  432. {
  433. // figure out the animtime when this was frame 0
  434. float dt = scene->GetTime() - event->GetStartTime();
  435. float seq_duration = SequenceDuration( info->m_nSequence );
  436. float flCycle = dt / seq_duration;
  437. flCycle = flCycle - (int)flCycle; // loop
  438. SetLayerCycle( info->m_iLayer, flCycle, flCycle );
  439. SetLayerPlaybackRate( info->m_iLayer, 0.0 );
  440. }
  441. else
  442. {
  443. SetLayerPlaybackRate( info->m_iLayer, 1.0 );
  444. }
  445. if (IsMoving())
  446. {
  447. info->m_flWeight = 0.0;
  448. }
  449. else
  450. {
  451. info->m_flWeight = 1.0;
  452. }
  453. return true;
  454. }
  455. bool CBaseFlex::HandleStartGestureSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event, CChoreoActor *actor )
  456. {
  457. Assert( info->m_iLayer == REQUEST_DEFERRED_LAYER_ALLOCATION );
  458. info->m_nSequence = LookupSequence( event->GetParameters() );
  459. info->m_iLayer = -1;
  460. if (info->m_nSequence < 0)
  461. {
  462. Warning( "CSceneEntity %s :\"%s\" unable to find gesture \"%s\"\n", STRING(GetEntityName()), actor->GetName(), event->GetParameters() );
  463. return false;
  464. }
  465. // FIXME: this seems like way too much code
  466. info->m_bIsGesture = false;
  467. KeyValues *seqKeyValues = GetSequenceKeyValues( info->m_nSequence );
  468. if (seqKeyValues)
  469. {
  470. // Do we have a build point section?
  471. KeyValues *pkvAllFaceposer = seqKeyValues->FindKey("faceposer");
  472. if ( pkvAllFaceposer )
  473. {
  474. KeyValues *pkvType = pkvAllFaceposer->FindKey("type");
  475. if (pkvType)
  476. {
  477. info->m_bIsGesture = (stricmp( pkvType->GetString(), "gesture" ) == 0) ? true : false;
  478. }
  479. }
  480. // FIXME: fixup tags that should be set as "linear", should be done in faceposer
  481. char szStartLoop[CEventAbsoluteTag::MAX_EVENTTAG_LENGTH] = { "loop" };
  482. char szEndLoop[CEventAbsoluteTag::MAX_EVENTTAG_LENGTH] = { "end" };
  483. // check in the tag indexes
  484. KeyValues *pkvFaceposer;
  485. for ( pkvFaceposer = pkvAllFaceposer->GetFirstSubKey(); pkvFaceposer; pkvFaceposer = pkvFaceposer->GetNextKey() )
  486. {
  487. if (!stricmp( pkvFaceposer->GetName(), "startloop" ))
  488. {
  489. strcpy( szStartLoop, pkvFaceposer->GetString() );
  490. }
  491. else if (!stricmp( pkvFaceposer->GetName(), "endloop" ))
  492. {
  493. strcpy( szEndLoop, pkvFaceposer->GetString() );
  494. }
  495. }
  496. CEventAbsoluteTag *ptag;
  497. ptag = event->FindAbsoluteTag( CChoreoEvent::ORIGINAL, szStartLoop );
  498. if (ptag)
  499. {
  500. ptag->SetLinear( true );
  501. }
  502. ptag = event->FindAbsoluteTag( CChoreoEvent::PLAYBACK, szStartLoop );
  503. if (ptag)
  504. {
  505. ptag->SetLinear( true );
  506. }
  507. ptag = event->FindAbsoluteTag( CChoreoEvent::ORIGINAL, szEndLoop );
  508. if (ptag)
  509. {
  510. ptag->SetLinear( true );
  511. }
  512. ptag = event->FindAbsoluteTag( CChoreoEvent::PLAYBACK, szEndLoop );
  513. if (ptag)
  514. {
  515. ptag->SetLinear( true );
  516. }
  517. if ( pkvAllFaceposer )
  518. {
  519. CStudioHdr *pstudiohdr = GetModelPtr();
  520. mstudioseqdesc_t &seqdesc = pstudiohdr->pSeqdesc( info->m_nSequence );
  521. mstudioanimdesc_t &animdesc = pstudiohdr->pAnimdesc( pstudiohdr->iRelativeAnim( info->m_nSequence, seqdesc.anim(0,0) ) );
  522. // check in the tag indexes
  523. KeyValues *pkvFaceposer;
  524. for ( pkvFaceposer = pkvAllFaceposer->GetFirstSubKey(); pkvFaceposer; pkvFaceposer = pkvFaceposer->GetNextKey() )
  525. {
  526. if (!stricmp( pkvFaceposer->GetName(), "tags" ))
  527. {
  528. KeyValues *pkvTags;
  529. for ( pkvTags = pkvFaceposer->GetFirstSubKey(); pkvTags; pkvTags = pkvTags->GetNextKey() )
  530. {
  531. int maxFrame = animdesc.numframes - 2; // FIXME: this is off by one!
  532. if ( maxFrame > 0)
  533. {
  534. float percentage = (float)pkvTags->GetInt() / maxFrame;
  535. CEventAbsoluteTag *ptag = event->FindAbsoluteTag( CChoreoEvent::ORIGINAL, pkvTags->GetName() );
  536. if (ptag)
  537. {
  538. if (fabs(ptag->GetPercentage() - percentage) > 0.05)
  539. {
  540. DevWarning("%s repositioned tag: %s : %.3f -> %.3f (%s:%s:%s)\n", scene->GetFilename(), pkvTags->GetName(), ptag->GetPercentage(), percentage, scene->GetFilename(), actor->GetName(), event->GetParameters() );
  541. // reposition tag
  542. ptag->SetPercentage( percentage );
  543. }
  544. }
  545. }
  546. }
  547. }
  548. }
  549. if (!event->VerifyTagOrder())
  550. {
  551. DevWarning("out of order tags : %s : (%s:%s:%s)\n", scene->GetFilename(), actor->GetName(), event->GetName(), event->GetParameters() );
  552. }
  553. }
  554. seqKeyValues->deleteThis();
  555. }
  556. // initialize posture suppression
  557. // FIXME: move priority of base animation so that layers can be inserted before
  558. // FIXME: query stopping, post idle layer to figure out correct weight
  559. // GetIdleLayerWeight()?
  560. if (!info->m_bIsGesture && IsMoving())
  561. {
  562. info->m_flWeight = 0.0;
  563. }
  564. else
  565. {
  566. info->m_flWeight = 1.0;
  567. }
  568. // this happens before StudioFrameAdvance()
  569. info->m_iPriority = actor->FindChannelIndex( event->GetChannel() );
  570. info->m_iLayer = AddLayeredSequence( info->m_nSequence, info->m_iPriority + GetScenePriority( scene ) );
  571. SetLayerNoRestore( info->m_iLayer, true );
  572. SetLayerDuration( info->m_iLayer, event->GetDuration() );
  573. SetLayerWeight( info->m_iLayer, 0.0 );
  574. bool looping = ((GetSequenceFlags( GetModelPtr(), info->m_nSequence ) & STUDIO_LOOPING) != 0);
  575. if ( looping )
  576. {
  577. DevMsg( 1, "vcd error, gesture %s of model %s is marked as STUDIO_LOOPING!\n",
  578. event->GetParameters(), STRING(GetModelName()) );
  579. }
  580. SetLayerLooping( info->m_iLayer, false ); // force to not loop
  581. float duration = event->GetDuration( );
  582. // figure out the animtime when this was frame 0
  583. float flEventCycle = (scene->GetTime() - event->GetStartTime()) / duration;
  584. float flCycle = event->GetOriginalPercentageFromPlaybackPercentage( flEventCycle );
  585. SetLayerCycle( info->m_iLayer, flCycle, 0.0 );
  586. SetLayerPlaybackRate( info->m_iLayer, 0.0 );
  587. return true;
  588. }
  589. bool CBaseFlex::StartFacingSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event, CChoreoActor *actor, CBaseEntity *pTarget )
  590. {
  591. if ( pTarget )
  592. {
  593. // Don't allow FACE commands while sitting in the vehicle
  594. CAI_BaseNPC *myNpc = MyNPCPointer();
  595. if ( myNpc && myNpc->IsInAVehicle() )
  596. return false;
  597. info->m_bIsMoving = false;
  598. return true;
  599. }
  600. return false;
  601. }
  602. bool CBaseFlex::StartMoveToSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event, CChoreoActor *actor, CBaseEntity *pTarget )
  603. {
  604. if (pTarget)
  605. {
  606. info->m_bIsMoving = false;
  607. info->m_bHasArrived = false;
  608. CAI_BaseNPC *myNpc = MyNPCPointer( );
  609. if (!myNpc)
  610. {
  611. return false;
  612. }
  613. EnterSceneSequence( scene, event, true );
  614. // If they're already moving, stop them
  615. //
  616. // Don't stop them during restore because that will set a stopping path very
  617. // nearby, causing us to signal arrival prematurely in CheckSceneEventCompletion.
  618. // BEWARE: the behavior of this bug depended on the order in which the entities were restored!!
  619. if ( myNpc->IsMoving() && !scene->IsRestoring() )
  620. {
  621. myNpc->GetNavigator()->StopMoving( false );
  622. }
  623. return true;
  624. }
  625. return false;
  626. }
  627. //-----------------------------------------------------------------------------
  628. // Purpose:
  629. //-----------------------------------------------------------------------------
  630. bool CBaseFlex::StartSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event, CChoreoActor *actor, CBaseEntity *pTarget )
  631. {
  632. switch ( event->GetType() )
  633. {
  634. case CChoreoEvent::SEQUENCE:
  635. return RequestStartSequenceSceneEvent( info, scene, event, actor, pTarget );
  636. case CChoreoEvent::GESTURE:
  637. return RequestStartGestureSceneEvent( info, scene, event, actor, pTarget );
  638. case CChoreoEvent::FACE:
  639. return StartFacingSceneEvent( info, scene, event, actor, pTarget );
  640. // FIXME: move this to an CBaseActor
  641. case CChoreoEvent::MOVETO:
  642. return StartMoveToSceneEvent( info, scene, event, actor, pTarget );
  643. case CChoreoEvent::LOOKAT:
  644. info->m_hTarget = pTarget;
  645. return true;
  646. case CChoreoEvent::FLEXANIMATION:
  647. info->InitWeight( this );
  648. return true;
  649. case CChoreoEvent::SPEAK:
  650. return true;
  651. case CChoreoEvent::EXPRESSION: // These are handled client-side
  652. return true;
  653. }
  654. return false;
  655. }
  656. //-----------------------------------------------------------------------------
  657. // Purpose: Remove expression
  658. // Input : scenefile -
  659. // expression -
  660. //-----------------------------------------------------------------------------
  661. void CBaseFlex::RemoveSceneEvent( CChoreoScene *scene, CChoreoEvent *event, bool fastKill )
  662. {
  663. Assert( event );
  664. for ( int i = 0 ; i < m_SceneEvents.Count(); i++ )
  665. {
  666. CSceneEventInfo *info = &m_SceneEvents[ i ];
  667. Assert( info );
  668. Assert( info->m_pEvent );
  669. if ( info->m_pScene != scene )
  670. continue;
  671. if ( info->m_pEvent != event)
  672. continue;
  673. if (ClearSceneEvent( info, fastKill, false ))
  674. {
  675. // Free this slot
  676. info->m_pEvent = NULL;
  677. info->m_pScene = NULL;
  678. info->m_bStarted = false;
  679. m_SceneEvents.Remove( i );
  680. return;
  681. }
  682. }
  683. // many events refuse to start due to bogus parameters
  684. }
  685. //-----------------------------------------------------------------------------
  686. // Purpose: Checks to see if the event should be considered "completed"
  687. //-----------------------------------------------------------------------------
  688. bool CBaseFlex::CheckSceneEvent( float currenttime, CChoreoScene *scene, CChoreoEvent *event )
  689. {
  690. for ( int i = 0 ; i < m_SceneEvents.Count(); i++ )
  691. {
  692. CSceneEventInfo *info = &m_SceneEvents[ i ];
  693. Assert( info );
  694. Assert( info->m_pEvent );
  695. if ( info->m_pScene != scene )
  696. continue;
  697. if ( info->m_pEvent != event)
  698. continue;
  699. return CheckSceneEventCompletion( info, currenttime, scene, event );
  700. }
  701. return true;
  702. }
  703. bool CBaseFlex::CheckSceneEventCompletion( CSceneEventInfo *info, float currenttime, CChoreoScene *scene, CChoreoEvent *event )
  704. {
  705. switch ( event->GetType() )
  706. {
  707. case CChoreoEvent::MOVETO:
  708. {
  709. CAI_BaseNPC *npc = MyNPCPointer( );
  710. if (npc)
  711. {
  712. // check movement, check arrival
  713. if (npc->GetNavigator()->IsGoalActive())
  714. {
  715. const Task_t *pCurTask = npc->GetTask();
  716. if ( pCurTask && (pCurTask->iTask == TASK_PLAY_SCENE || pCurTask->iTask == TASK_WAIT_FOR_MOVEMENT ) )
  717. {
  718. float preload = event->GetEndTime() - currenttime;
  719. if (preload < 0)
  720. {
  721. //Msg("%.1f: no preload\n", currenttime );
  722. return false;
  723. }
  724. float t = npc->GetTimeToNavGoal();
  725. // Msg("%.1f: preload (%s:%.1f) %.1f %.1f\n", currenttime, event->GetName(), event->GetEndTime(), preload, t );
  726. // FIXME: t is zero if no path can be built!
  727. if (t > 0.0f && t <= preload)
  728. {
  729. return true;
  730. }
  731. return false;
  732. }
  733. }
  734. else if (info->m_bHasArrived)
  735. {
  736. return true;
  737. }
  738. else if (info->m_bStarted && !npc->IsCurSchedule( SCHED_SCENE_GENERIC ))
  739. {
  740. // FIXME: There's still a hole in the logic is the save happens immediately after the SS steals the npc but before their AI has run again
  741. Warning( "%s : %8.2f: waiting for actor %s to complete MOVETO but actor not in SCHED_SCENE_GENERIC\n", scene->GetFilename(), scene->GetTime(), event->GetActor()->GetName() );
  742. // no longer in a scene :P
  743. return true;
  744. }
  745. // still trying
  746. return false;
  747. }
  748. }
  749. break;
  750. default:
  751. break;
  752. }
  753. return true;
  754. }
  755. //-----------------------------------------------------------------------------
  756. // Purpose: Default implementation
  757. //-----------------------------------------------------------------------------
  758. void CBaseFlex::ProcessSceneEvents( void )
  759. {
  760. VPROF( "CBaseFlex::ProcessSceneEvents" );
  761. // slowly decay to netural expression
  762. for ( LocalFlexController_t i = LocalFlexController_t(0); i < GetNumFlexControllers(); i++)
  763. {
  764. SetFlexWeight( i, GetFlexWeight( i ) * 0.95 );
  765. }
  766. bool bHasForegroundEvents = false;
  767. // Iterate SceneEvents and look for active slots
  768. for ( int i = 0; i < m_SceneEvents.Count(); i++ )
  769. {
  770. CSceneEventInfo *info = &m_SceneEvents[ i ];
  771. Assert( info );
  772. // FIXME: Need a safe handle to m_pEvent in case of memory deletion?
  773. CChoreoEvent *event = info->m_pEvent;
  774. Assert( event );
  775. CChoreoScene *scene = info->m_pScene;
  776. Assert( scene );
  777. if ( scene && !scene->IsBackground() )
  778. {
  779. bHasForegroundEvents = true;
  780. }
  781. if (ProcessSceneEvent( info, scene, event ))
  782. {
  783. info->m_bStarted = true;
  784. }
  785. }
  786. if ( bHasForegroundEvents && scene_showunlock.GetBool())
  787. {
  788. CAI_BaseNPC *myNpc = MyNPCPointer( );
  789. if ( myNpc && !(myNpc->GetState() == NPC_STATE_SCRIPT || myNpc->IsCurSchedule( SCHED_SCENE_GENERIC )) )
  790. {
  791. Vector p0 = myNpc->GetHullMins();
  792. Vector p1 = myNpc->GetHullMaxs();
  793. p0.z = p1.z + 2;
  794. p1.z = p1.z + 2;
  795. NDebugOverlay::Box( myNpc->GetAbsOrigin(), p0, p1, 255, 0, 0, 0, 0.12 );
  796. }
  797. }
  798. // any needed layer priorites have now been reset
  799. m_bUpdateLayerPriorities = false;
  800. }
  801. class CFlexSceneFileManager : CAutoGameSystem
  802. {
  803. public:
  804. CFlexSceneFileManager( char const *name ) : CAutoGameSystem( name )
  805. {
  806. }
  807. virtual bool InitRecursive( const char *pFolder )
  808. {
  809. if ( pFolder == NULL )
  810. {
  811. pFolder = "expressions";
  812. #if defined( PORTAL2 )
  813. if ( IsGameConsole() )
  814. {
  815. return false;
  816. }
  817. #endif
  818. }
  819. char directory[ MAX_PATH ];
  820. Q_snprintf( directory, sizeof( directory ), "%s/*.*", pFolder );
  821. FileFindHandle_t fh;
  822. const char *fn;
  823. for (fn = g_pFullFileSystem->FindFirst( directory, &fh ); fn; fn = g_pFullFileSystem->FindNext( fh ) )
  824. {
  825. if ( !stricmp( fn, ".") || !stricmp( fn, "..") )
  826. {
  827. continue;
  828. }
  829. if ( g_pFullFileSystem->FindIsDirectory( fh ) )
  830. {
  831. char folderpath[MAX_PATH];
  832. Q_snprintf( folderpath, sizeof( folderpath ), "%s/%s", pFolder, fn );
  833. InitRecursive( folderpath );
  834. continue;
  835. }
  836. const char *pExt = Q_GetFileExtension( fn );
  837. if ( pExt && Q_stricmp( pExt, "vfe" ) )
  838. {
  839. continue;
  840. }
  841. char fullFileName[MAX_PATH];
  842. Q_snprintf( fullFileName, sizeof(fullFileName), "%s/%s", pFolder, fn );
  843. // strip default folder and extension
  844. int index = V_strlen( "expressions/" );
  845. char vfeName[ MAX_PATH ];
  846. V_StripExtension( &fullFileName[index], vfeName, sizeof( vfeName ) );
  847. V_FixSlashes( vfeName );
  848. FindSceneFile( NULL, vfeName, true );
  849. }
  850. return true;
  851. }
  852. virtual bool Init()
  853. {
  854. // Trakcer 16692: Preload these at startup to avoid hitch first time we try to load them during actual gameplay
  855. FindSceneFile( NULL, "phonemes", true );
  856. FindSceneFile( NULL, "phonemes_weak", true );
  857. FindSceneFile( NULL, "phonemes_strong", true );
  858. #if defined( HL2_DLL )
  859. FindSceneFile( NULL, "random", true );
  860. FindSceneFile( NULL, "randomAlert", true );
  861. #endif
  862. InitRecursive( NULL );
  863. return true;
  864. }
  865. // Tracker 14992: We used to load 18K of .vfes for every CBaseFlex who lipsynced, but now we only load those files once globally.
  866. // Note, we could wipe these between levels, but they don't ever load more than the weak/normal/strong phoneme classes that I can tell
  867. // so I'll just leave them loaded forever for now
  868. virtual void Shutdown()
  869. {
  870. DeleteSceneFiles();
  871. }
  872. //-----------------------------------------------------------------------------
  873. // Purpose: Sets up translations
  874. // Input : *instance -
  875. // *pSettinghdr -
  876. // Output : void
  877. //-----------------------------------------------------------------------------
  878. void EnsureTranslations( CBaseFlex *instance, const flexsettinghdr_t *pSettinghdr )
  879. {
  880. // The only time instance is NULL is in Init() above, where we're just loading the .vfe files off of the hard disk.
  881. if ( instance )
  882. {
  883. instance->EnsureTranslations( pSettinghdr );
  884. }
  885. }
  886. const void *FindSceneFile( CBaseFlex *instance, const char *filename, bool allowBlockingIO )
  887. {
  888. char szFilename[MAX_PATH];
  889. Assert( V_strlen( filename ) < MAX_PATH );
  890. V_strcpy( szFilename, filename );
  891. Q_FixSlashes( szFilename );
  892. // See if it's already loaded
  893. int i;
  894. for ( i = 0; i < m_FileList.Count(); i++ )
  895. {
  896. CFlexSceneFile *file = m_FileList[ i ];
  897. if ( file && !V_stricmp( file->filename, szFilename ) )
  898. {
  899. // Make sure translations (local to global flex controller) are set up for this instance
  900. EnsureTranslations( instance, ( const flexsettinghdr_t * )file->buffer );
  901. return file->buffer;
  902. }
  903. }
  904. if ( !allowBlockingIO )
  905. {
  906. return NULL;
  907. }
  908. // Load file into memory
  909. void *buffer = NULL;
  910. int len = filesystem->ReadFileEx( UTIL_VarArgs( "expressions/%s.vfe", szFilename ), "GAME", &buffer, false, true );
  911. if ( !len )
  912. return NULL;
  913. // Create scene entry
  914. CFlexSceneFile *pfile = new CFlexSceneFile;
  915. // Remember filename
  916. Q_strncpy( pfile->filename, szFilename, sizeof( pfile->filename ) );
  917. // Remember data pointer
  918. pfile->buffer = buffer;
  919. // Add to list
  920. m_FileList.AddToTail( pfile );
  921. // Swap the entire file
  922. if ( IsGameConsole() )
  923. {
  924. CByteswap swap;
  925. swap.ActivateByteSwapping( true );
  926. byte *pData = (byte*)buffer;
  927. flexsettinghdr_t *pHdr = (flexsettinghdr_t*)pData;
  928. swap.SwapFieldsToTargetEndian( pHdr );
  929. // Flex Settings
  930. flexsetting_t *pFlexSetting = (flexsetting_t*)((byte*)pHdr + pHdr->flexsettingindex);
  931. for ( int i = 0; i < pHdr->numflexsettings; ++i, ++pFlexSetting )
  932. {
  933. swap.SwapFieldsToTargetEndian( pFlexSetting );
  934. flexweight_t *pWeight = (flexweight_t*)(((byte*)pFlexSetting) + pFlexSetting->settingindex );
  935. for ( int j = 0; j < pFlexSetting->numsettings; ++j, ++pWeight )
  936. {
  937. swap.SwapFieldsToTargetEndian( pWeight );
  938. }
  939. }
  940. // indexes
  941. pData = (byte*)pHdr + pHdr->indexindex;
  942. swap.SwapBufferToTargetEndian( (int*)pData, (int*)pData, pHdr->numindexes );
  943. // keymappings
  944. pData = (byte*)pHdr + pHdr->keymappingindex;
  945. swap.SwapBufferToTargetEndian( (int*)pData, (int*)pData, pHdr->numkeys );
  946. // keyname indices
  947. pData = (byte*)pHdr + pHdr->keynameindex;
  948. swap.SwapBufferToTargetEndian( (int*)pData, (int*)pData, pHdr->numkeys );
  949. }
  950. // Fill in translation table
  951. EnsureTranslations( instance, ( const flexsettinghdr_t * )pfile->buffer );
  952. // Return data
  953. return pfile->buffer;
  954. }
  955. private:
  956. void DeleteSceneFiles()
  957. {
  958. while ( m_FileList.Count() > 0 )
  959. {
  960. CFlexSceneFile *file = m_FileList[ 0 ];
  961. m_FileList.Remove( 0 );
  962. filesystem->FreeOptimalReadBuffer( file->buffer );
  963. delete file;
  964. }
  965. }
  966. CUtlVector< CFlexSceneFile * > m_FileList;
  967. };
  968. // Singleton manager
  969. CFlexSceneFileManager g_FlexSceneFileManager( "CFlexSceneFileManager" );
  970. //-----------------------------------------------------------------------------
  971. // Purpose: Each CBaseFlex maintains a UtlRBTree of mappings, one for each loaded flex scene file it uses. This is used to
  972. // sort the entries in the RBTree
  973. // Input : lhs -
  974. // rhs -
  975. // Output : Returns true on success, false on failure.
  976. //-----------------------------------------------------------------------------
  977. bool CBaseFlex::FlexSettingLessFunc( const FS_LocalToGlobal_t& lhs, const FS_LocalToGlobal_t& rhs )
  978. {
  979. return lhs.m_Key < rhs.m_Key;
  980. }
  981. //-----------------------------------------------------------------------------
  982. // Purpose: Since everyone shared a pSettinghdr now, we need to set up the localtoglobal mapping per entity, but
  983. // we just do this in memory with an array of integers (could be shorts, I suppose)
  984. // Input : *pSettinghdr -
  985. //-----------------------------------------------------------------------------
  986. void CBaseFlex::EnsureTranslations( const flexsettinghdr_t *pSettinghdr )
  987. {
  988. Assert( pSettinghdr );
  989. FS_LocalToGlobal_t entry( pSettinghdr );
  990. unsigned short idx = m_LocalToGlobal.Find( entry );
  991. if ( idx != m_LocalToGlobal.InvalidIndex() )
  992. return;
  993. entry.SetCount( pSettinghdr->numkeys );
  994. for ( int i = 0; i < pSettinghdr->numkeys; ++i )
  995. {
  996. entry.m_Mapping[ i ] = FindFlexController( pSettinghdr->pLocalName( i ) );
  997. }
  998. m_LocalToGlobal.Insert( entry );
  999. }
  1000. //-----------------------------------------------------------------------------
  1001. // Purpose: Look up instance specific mapping
  1002. // Input : *pSettinghdr -
  1003. // key -
  1004. // Output : int
  1005. //-----------------------------------------------------------------------------
  1006. LocalFlexController_t CBaseFlex::FlexControllerLocalToGlobal( const flexsettinghdr_t *pSettinghdr, int key )
  1007. {
  1008. FS_LocalToGlobal_t entry( pSettinghdr );
  1009. int idx = m_LocalToGlobal.Find( entry );
  1010. if ( idx == m_LocalToGlobal.InvalidIndex() )
  1011. {
  1012. // This should never happen!!!
  1013. Assert( 0 );
  1014. Warning( "Unable to find mapping for flexcontroller %i, settings %p on %i/%s\n", key, pSettinghdr, entindex(), GetClassname() );
  1015. EnsureTranslations( pSettinghdr );
  1016. idx = m_LocalToGlobal.Find( entry );
  1017. if ( idx == m_LocalToGlobal.InvalidIndex() )
  1018. {
  1019. Error( "CBaseFlex::FlexControllerLocalToGlobal failed!\n" );
  1020. }
  1021. }
  1022. FS_LocalToGlobal_t& result = m_LocalToGlobal[ idx ];
  1023. // Validate lookup
  1024. Assert( result.m_nCount != 0 && key < result.m_nCount );
  1025. LocalFlexController_t index = result.m_Mapping[ key ];
  1026. return index;
  1027. }
  1028. //-----------------------------------------------------------------------------
  1029. // Purpose:
  1030. // Input : *filename -
  1031. //-----------------------------------------------------------------------------
  1032. const void *CBaseFlex::FindSceneFile( const char *filename )
  1033. {
  1034. // Ask manager to get the globally cached scene instead.
  1035. // hunt up the tree for the filename starting with our model name prepended as the path
  1036. static char szExtendedPath[MAX_PATH];
  1037. if ( StringHasPrefix( STRING( GetModelName() ), "models" ) )
  1038. {
  1039. strcpy( szExtendedPath, StringAfterPrefix( STRING( GetModelName() ), "models" ) + 1 );
  1040. }
  1041. else
  1042. {
  1043. strcpy( szExtendedPath, STRING( GetModelName() ) );
  1044. }
  1045. V_StripExtension( szExtendedPath, szExtendedPath, sizeof( szExtendedPath ) );
  1046. V_FixupPathName( szExtendedPath, sizeof( szExtendedPath ), szExtendedPath );
  1047. const void *pSceneFile = NULL;
  1048. // FIXME: V_StripLastDir returns "./" path when it strips out the last one. That don't resolve on FindSceneFile
  1049. while ( V_strlen( szExtendedPath ) > 2 )
  1050. {
  1051. static char szExtendedName[MAX_PATH];
  1052. V_ComposeFileName( szExtendedPath, filename, szExtendedName, sizeof( szExtendedName ) );
  1053. pSceneFile = g_FlexSceneFileManager.FindSceneFile( this, szExtendedName, false );
  1054. if ( pSceneFile )
  1055. break;
  1056. if ( !V_StripLastDir( szExtendedPath, sizeof( szExtendedPath ) ) )
  1057. break;
  1058. }
  1059. if ( !pSceneFile )
  1060. {
  1061. // just ask for it by name
  1062. pSceneFile = g_FlexSceneFileManager.FindSceneFile( this, filename, false );
  1063. }
  1064. return pSceneFile;
  1065. }
  1066. ConVar ai_expression_optimization( "ai_expression_optimization", "0", FCVAR_NONE, "Disable npc background expressions when you can't see them." );
  1067. ConVar ai_expression_frametime( "ai_expression_frametime", "0.05", FCVAR_NONE, "Maximum frametime to still play background expressions." );
  1068. //-----------------------------------------------------------------------------
  1069. // Various methods to process facial SceneEvents:
  1070. //-----------------------------------------------------------------------------
  1071. bool CBaseFlex::ProcessFlexAnimationSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event )
  1072. {
  1073. VPROF( "CBaseFlex::ProcessFlexAnimationSceneEvent" );
  1074. if ( event->HasEndTime() )
  1075. {
  1076. // don't bother with flex animation if the player can't see you
  1077. CAI_BaseNPC *myNpc = MyNPCPointer( );
  1078. if (myNpc)
  1079. {
  1080. if (!myNpc->HasCondition( COND_IN_PVS ))
  1081. return true;
  1082. if (ai_expression_optimization.GetBool())
  1083. {
  1084. if (scene->IsBackground())
  1085. {
  1086. // if framerate too slow, disable
  1087. if (gpGlobals->frametime > ai_expression_frametime.GetFloat())
  1088. {
  1089. info->m_bHasArrived = true;
  1090. info->m_flNext = gpGlobals->curtime + RandomFloat( 0.7, 1.2 );
  1091. }
  1092. // only check occasionally
  1093. else if (info->m_flNext <= gpGlobals->curtime)
  1094. {
  1095. CBasePlayer *pPlayer = UTIL_GetLocalPlayer();
  1096. // if not in view, disable
  1097. info->m_bHasArrived = (pPlayer && !pPlayer->FInViewCone( this ) );
  1098. info->m_flNext = gpGlobals->curtime + RandomFloat( 0.7, 1.2 );
  1099. }
  1100. if (info->m_bHasArrived)
  1101. {
  1102. // NDebugOverlay::Box( myNpc->GetAbsOrigin(), myNpc->GetHullMins(), myNpc->GetHullMaxs(), 255, 0, 0, 0, 0.22 );
  1103. return true;
  1104. }
  1105. // NDebugOverlay::Box( myNpc->GetAbsOrigin(), myNpc->GetHullMins(), myNpc->GetHullMaxs(), 0, 255, 0, 0, 0.22 );
  1106. }
  1107. }
  1108. }
  1109. AddFlexAnimation( info );
  1110. }
  1111. return true;
  1112. }
  1113. bool CBaseFlex::ProcessFlexSettingSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event )
  1114. {
  1115. // Flexanimations have to have an end time!!!
  1116. if ( !event->HasEndTime() )
  1117. return true;
  1118. VPROF( "CBaseFlex::ProcessFlexSettingSceneEvent" );
  1119. // Look up the actual strings
  1120. const char *scenefile = event->GetParameters();
  1121. const char *name = event->GetParameters2();
  1122. // Have to find both strings
  1123. if ( scenefile && name )
  1124. {
  1125. if ( info->m_pExpHdr == NULL)
  1126. {
  1127. info->m_pExpHdr = ( const flexsettinghdr_t * )FindSceneFile( scenefile );
  1128. }
  1129. if ( info->m_pExpHdr )
  1130. {
  1131. float scenetime = scene->GetTime();
  1132. float scale = event->GetIntensity( scenetime );
  1133. // Add the named expression
  1134. AddFlexSetting( name, scale, info->m_pExpHdr, !info->m_bStarted );
  1135. }
  1136. }
  1137. return true;
  1138. }
  1139. bool CBaseFlex::ProcessFacingSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event )
  1140. {
  1141. // make sure target exists
  1142. if (info->m_hTarget == NULL)
  1143. return false;
  1144. VPROF( "CBaseFlex::ProcessFacingSceneEvent" );
  1145. // make sure we're still able to play this command
  1146. if (!EnterSceneSequence( scene, event, true ))
  1147. {
  1148. return false;
  1149. }
  1150. if (!info->m_bStarted)
  1151. {
  1152. info->m_flInitialYaw = GetLocalAngles().y;
  1153. }
  1154. // lock in place if aiming at self
  1155. if (info->m_hTarget == this)
  1156. {
  1157. return true;
  1158. }
  1159. CAI_BaseNPC *myNpc = MyNPCPointer( );
  1160. if (myNpc)
  1161. {
  1162. if (info->m_bIsMoving != IsMoving())
  1163. {
  1164. info->m_flInitialYaw = GetLocalAngles().y;
  1165. }
  1166. info->m_bIsMoving = IsMoving();
  1167. // Msg("%f : %f - %f\n", scene->GetTime(), event->GetStartTime(), event->GetEndTime() );
  1168. // FIXME: why are the splines ill behaved at the end?
  1169. float intensity = event->GetIntensity( scene->GetTime() );
  1170. if (info->m_bIsMoving)
  1171. {
  1172. myNpc->AddFacingTarget( info->m_hTarget, intensity, 0.2 );
  1173. }
  1174. else
  1175. {
  1176. float goalYaw = myNpc->CalcIdealYaw( info->m_hTarget->EyePosition() );
  1177. float diff = UTIL_AngleDiff( goalYaw, info->m_flInitialYaw );
  1178. float idealYaw = UTIL_AngleMod( info->m_flInitialYaw + diff * intensity );
  1179. // Msg("yaw %.1f : %.1f (%.1f)\n", info->m_flInitialYaw, idealYaw, intensity );
  1180. myNpc->GetMotor()->SetIdealYawAndUpdate( idealYaw );
  1181. }
  1182. return true;
  1183. }
  1184. return false;
  1185. }
  1186. static Activity DetermineExpressionMoveActivity( CChoreoEvent *event, CAI_BaseNPC *pNPC )
  1187. {
  1188. Activity activity = ACT_WALK;
  1189. const char *sParam2 = event->GetParameters2();
  1190. if ( !sParam2 || !sParam2[0] )
  1191. return activity;
  1192. // Custom distance styles are appended to param2 with a space as a separator
  1193. const char *pszAct = Q_strstr( sParam2, " " );
  1194. if ( pszAct )
  1195. {
  1196. char szActName[256];
  1197. Q_strncpy( szActName, sParam2, sizeof(szActName) );
  1198. szActName[ (pszAct-sParam2) ] = '\0';
  1199. pszAct = szActName;
  1200. }
  1201. else
  1202. {
  1203. pszAct = sParam2;
  1204. }
  1205. if ( !Q_stricmp( pszAct, "Walk" ) )
  1206. {
  1207. activity = ACT_WALK;
  1208. }
  1209. else if ( !Q_stricmp( pszAct, "Run" ) )
  1210. {
  1211. activity = ACT_RUN;
  1212. }
  1213. else if ( !Q_stricmp( pszAct, "CrouchWalk" ) )
  1214. {
  1215. activity = ACT_WALK_CROUCH;
  1216. }
  1217. else
  1218. {
  1219. // Try and resolve the activity name
  1220. activity = (Activity)ActivityList_IndexForName( pszAct );
  1221. if ( activity == ACT_INVALID )
  1222. {
  1223. // Assume it's a sequence name
  1224. pNPC->m_iszSceneCustomMoveSeq = AllocPooledString( pszAct );
  1225. activity = ACT_SCRIPT_CUSTOM_MOVE;
  1226. }
  1227. }
  1228. return activity;
  1229. }
  1230. bool CBaseFlex::ProcessMoveToSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event )
  1231. {
  1232. // make sure target exists
  1233. if (info->m_hTarget == NULL)
  1234. return false;
  1235. // FIXME: move to CBaseActor or BaseNPC
  1236. CAI_BaseNPC *myNpc = MyNPCPointer( );
  1237. if (!myNpc)
  1238. return false;
  1239. VPROF( "CBaseFlex::ProcessMoveToSceneEvent" );
  1240. // make sure we're still able to play this command
  1241. if (!EnterSceneSequence( scene, event, true ))
  1242. {
  1243. return false;
  1244. }
  1245. // lock in place if aiming at self
  1246. if (info->m_hTarget == this)
  1247. {
  1248. return true;
  1249. }
  1250. // If we're in a vehicle, make us exit and *then* begin the run
  1251. if ( myNpc->IsInAVehicle() )
  1252. {
  1253. // Make us exit and wait
  1254. myNpc->ExitVehicle();
  1255. return false;
  1256. }
  1257. const Task_t *pCurTask = myNpc->GetTask();
  1258. if (!info->m_bIsMoving && (!IsMoving() || pCurTask->iTask == TASK_STOP_MOVING) )
  1259. {
  1260. if ( pCurTask && (pCurTask->iTask == TASK_PLAY_SCENE || pCurTask->iTask == TASK_WAIT_FOR_MOVEMENT || pCurTask->iTask == TASK_STOP_MOVING ) )
  1261. {
  1262. Activity moveActivity = DetermineExpressionMoveActivity( event, myNpc );
  1263. // AI_NavGoal_t goal( info->m_hTarget->EyePosition(), moveActivity, AIN_HULL_TOLERANCE );
  1264. myNpc->SetTarget( info->m_hTarget );
  1265. float flDistTolerance;
  1266. flDistTolerance = myNpc->GetHullWidth() / 2.0;
  1267. // flDistTolerance = AIN_HULL_TOLERANCE;
  1268. if (event->m_bForceShortMovement)
  1269. {
  1270. flDistTolerance = 0.1f;
  1271. }
  1272. AI_NavGoal_t goal( GOALTYPE_TARGETENT, moveActivity, flDistTolerance, AIN_UPDATE_TARGET_POS );
  1273. float flDist = (info->m_hTarget->EyePosition() - GetAbsOrigin()).Length2D();
  1274. if (flDist > MAX( MAX( flDistTolerance, 0.1 ), event->GetDistanceToTarget()))
  1275. {
  1276. // Msg("flDist %.1f\n", flDist );
  1277. int result = false;
  1278. if ( !myNpc->IsUnreachable( info->m_hTarget ) )
  1279. {
  1280. result = myNpc->GetNavigator()->SetGoal( goal, AIN_CLEAR_TARGET );
  1281. if ( !result )
  1282. {
  1283. myNpc->RememberUnreachable( info->m_hTarget, 1.5 );
  1284. }
  1285. }
  1286. if (result)
  1287. {
  1288. myNpc->GetNavigator()->SetMovementActivity( moveActivity );
  1289. myNpc->GetNavigator()->SetArrivalDistance( event->GetDistanceToTarget() );
  1290. info->m_bIsMoving = true;
  1291. }
  1292. else
  1293. {
  1294. // need route build failure case
  1295. // Msg("actor %s unable to build route\n", STRING( myNpc->GetEntityName() ) );
  1296. // Assert(0);
  1297. if (developer.GetInt() > 0 && scene_showmoveto.GetBool())
  1298. {
  1299. Vector vTestPoint;
  1300. myNpc->GetMoveProbe()->FloorPoint( info->m_hTarget->EyePosition(), myNpc->GetAITraceMask(), 0, -64, &vTestPoint );
  1301. NDebugOverlay::HorzArrow( GetAbsOrigin() + Vector( 0, 0, 1 ), vTestPoint + Vector( 0, 0, 1 ), 4, 255, 0, 255, 0, false, 0.12 );
  1302. NDebugOverlay::Box( vTestPoint, myNpc->GetHullMins(), myNpc->GetHullMaxs(), 255, 0, 255, 0, 0.12 );
  1303. }
  1304. }
  1305. }
  1306. else
  1307. {
  1308. info->m_bHasArrived = true;
  1309. }
  1310. }
  1311. }
  1312. else if (IsMoving())
  1313. {
  1314. // float flDist = (myNpc->GetNavigator()->GetGoalPos() - GetAbsOrigin()).Length2D();
  1315. float flDist = (info->m_hTarget->EyePosition() - GetAbsOrigin()).Length2D();
  1316. if (flDist <= event->GetDistanceToTarget())
  1317. {
  1318. myNpc->GetNavigator()->StopMoving( false ); // Stop moving
  1319. info->m_bHasArrived = true;
  1320. }
  1321. }
  1322. else
  1323. {
  1324. info->m_bIsMoving = false;
  1325. }
  1326. // show movement target
  1327. if (developer.GetInt() > 0 && scene_showmoveto.GetBool() && IsMoving())
  1328. {
  1329. Vector vecStart, vTestPoint;
  1330. vecStart = myNpc->GetNavigator()->GetGoalPos();
  1331. myNpc->GetMoveProbe()->FloorPoint( vecStart, myNpc->GetAITraceMask(), 0, -64, &vTestPoint );
  1332. int r, g, b;
  1333. r = b = g = 0;
  1334. if ( myNpc->GetNavigator()->CanFitAtPosition( vTestPoint, myNpc->GetAITraceMask() ) )
  1335. {
  1336. if ( myNpc->GetMoveProbe()->CheckStandPosition( vTestPoint, myNpc->GetAITraceMask() ) )
  1337. {
  1338. if (event->IsResumeCondition())
  1339. {
  1340. g = 255;
  1341. }
  1342. else
  1343. {
  1344. r = 255; g = 255;
  1345. }
  1346. }
  1347. else
  1348. {
  1349. b = 255; g = 255;
  1350. }
  1351. }
  1352. else
  1353. {
  1354. r = 255;
  1355. }
  1356. NDebugOverlay::HorzArrow( GetAbsOrigin() + Vector( 0, 0, 1 ), vTestPoint + Vector( 0, 0, 1 ), 4, r, g, b, 0, false, 0.12 );
  1357. NDebugOverlay::Box( vTestPoint, myNpc->GetHullMins(), myNpc->GetHullMaxs(), r, g, b, 0, 0.12 );
  1358. }
  1359. // handled in task
  1360. return true;
  1361. }
  1362. bool CBaseFlex::ProcessLookAtSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event )
  1363. {
  1364. VPROF( "CBaseFlex::ProcessLookAtSceneEvent" );
  1365. CAI_BaseNPC *myNpc = MyNPCPointer( );
  1366. if (myNpc && info->m_hTarget != NULL)
  1367. {
  1368. float intensity = event->GetIntensity( scene->GetTime() );
  1369. // clamp in-ramp to 0.3 seconds
  1370. float flDuration = scene->GetTime() - event->GetStartTime();
  1371. float flMaxIntensity = flDuration < 0.3f ? SimpleSpline( flDuration / 0.3f ) : 1.0f;
  1372. intensity = clamp( intensity, 0.0f, flMaxIntensity );
  1373. myNpc->AddLookTarget( info->m_hTarget, intensity, 0.1 );
  1374. if (developer.GetInt() > 0 && scene_showlook.GetBool() && info->m_hTarget)
  1375. {
  1376. Vector tmp = info->m_hTarget->EyePosition() - myNpc->EyePosition();
  1377. VectorNormalize( tmp );
  1378. Vector p0 = myNpc->EyePosition();
  1379. NDebugOverlay::VertArrow( p0, p0 + tmp * (4 + 16 * intensity ), 4, 255, 255, 255, 0, true, 0.12 );
  1380. }
  1381. }
  1382. return true;
  1383. }
  1384. //-----------------------------------------------------------------------------
  1385. // Purpose:
  1386. //-----------------------------------------------------------------------------
  1387. bool CBaseFlex::ProcessSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event )
  1388. {
  1389. VPROF( "CBaseFlex::ProcessSceneEvent" );
  1390. switch ( event->GetType() )
  1391. {
  1392. case CChoreoEvent::FLEXANIMATION:
  1393. return ProcessFlexAnimationSceneEvent( info, scene, event );
  1394. case CChoreoEvent::EXPRESSION:
  1395. return ProcessFlexSettingSceneEvent( info, scene, event );
  1396. case CChoreoEvent::SEQUENCE:
  1397. return ProcessSequenceSceneEvent( info, scene, event );
  1398. case CChoreoEvent::GESTURE:
  1399. return ProcessGestureSceneEvent( info, scene, event );
  1400. case CChoreoEvent::FACE:
  1401. return ProcessFacingSceneEvent( info, scene, event );
  1402. case CChoreoEvent::MOVETO:
  1403. return ProcessMoveToSceneEvent( info, scene, event );
  1404. case CChoreoEvent::LOOKAT:
  1405. return ProcessLookAtSceneEvent( info, scene, event );
  1406. case CChoreoEvent::SPEAK:
  1407. return true;
  1408. default:
  1409. {
  1410. Msg( "unknown type %d in ProcessSceneEvent()\n", event->GetType() );
  1411. Assert( 0 );
  1412. }
  1413. }
  1414. return false;
  1415. }
  1416. //-----------------------------------------------------------------------------
  1417. // Purpose:
  1418. //-----------------------------------------------------------------------------
  1419. bool CBaseFlex::IsRunningSceneMoveToEvent()
  1420. {
  1421. for ( int i = m_SceneEvents.Count() - 1; i >= 0; i-- )
  1422. {
  1423. CSceneEventInfo *info = &m_SceneEvents[ i ];
  1424. CChoreoEvent *event = info->m_pEvent;
  1425. if ( event && event->GetType() == CChoreoEvent::MOVETO )
  1426. return true;
  1427. }
  1428. return false;
  1429. }
  1430. flexsetting_t const *CBaseFlex::FindNamedSetting( flexsettinghdr_t const *pSettinghdr, const char *expr )
  1431. {
  1432. int i;
  1433. const flexsetting_t *pSetting = NULL;
  1434. for ( i = 0; i < pSettinghdr->numflexsettings; i++ )
  1435. {
  1436. pSetting = pSettinghdr->pSetting( i );
  1437. if ( !pSetting )
  1438. continue;
  1439. const char *name = pSetting->pszName();
  1440. if ( !stricmp( name, expr ) )
  1441. break;
  1442. }
  1443. if ( i>=pSettinghdr->numflexsettings )
  1444. {
  1445. return NULL;
  1446. }
  1447. return pSetting;
  1448. }
  1449. //-----------------------------------------------------------------------------
  1450. // Purpose:
  1451. // Input : *event -
  1452. //-----------------------------------------------------------------------------
  1453. void CBaseFlex::AddFlexAnimation( CSceneEventInfo *info )
  1454. {
  1455. if ( !info )
  1456. return;
  1457. // don't bother with flex animation if the player can't see you
  1458. CAI_BaseNPC *myNpc = MyNPCPointer( );
  1459. if (myNpc && !myNpc->HasCondition( COND_IN_PVS ))
  1460. return;
  1461. CChoreoEvent *event = info->m_pEvent;
  1462. if ( !event )
  1463. return;
  1464. CChoreoScene *scene = info->m_pScene;
  1465. if ( !scene )
  1466. return;
  1467. if ( !event->GetTrackLookupSet() )
  1468. {
  1469. // Create lookup data
  1470. for ( int i = 0; i < event->GetNumFlexAnimationTracks(); i++ )
  1471. {
  1472. CFlexAnimationTrack *track = event->GetFlexAnimationTrack( i );
  1473. if ( !track )
  1474. continue;
  1475. if ( track->IsComboType() )
  1476. {
  1477. char name[ 512 ];
  1478. Q_strncpy( name, "right_" ,sizeof(name));
  1479. Q_strncat( name, track->GetFlexControllerName(),sizeof(name), COPY_ALL_CHARACTERS );
  1480. track->SetFlexControllerIndex( FindFlexController( name ), 0, 0 );
  1481. if ( CAI_BaseActor::IsServerSideFlexController( name ) )
  1482. {
  1483. Assert( !"Should stereo controllers ever be server side only?" );
  1484. track->SetServerSide( true );
  1485. }
  1486. Q_strncpy( name, "left_" ,sizeof(name));
  1487. Q_strncat( name, track->GetFlexControllerName(),sizeof(name), COPY_ALL_CHARACTERS );
  1488. track->SetFlexControllerIndex( FindFlexController( name ), 0, 1 );
  1489. if ( CAI_BaseActor::IsServerSideFlexController( name ) )
  1490. {
  1491. Assert( !"Should stereo controllers ever be server side only?" );
  1492. track->SetServerSide( true );
  1493. }
  1494. }
  1495. else
  1496. {
  1497. track->SetFlexControllerIndex( FindFlexController( (char *)track->GetFlexControllerName() ), 0 );
  1498. // Only non-combo tracks can be server side
  1499. track->SetServerSide( CAI_BaseActor::IsServerSideFlexController( track->GetFlexControllerName() ) );
  1500. }
  1501. }
  1502. event->SetTrackLookupSet( true );
  1503. }
  1504. float scenetime = scene->GetTime();
  1505. // decay if this is a background scene and there's other flex animations playing
  1506. float weight = event->GetIntensity( scenetime ) * info->UpdateWeight( this );
  1507. {
  1508. VPROF( "AddFlexAnimation_SetFlexWeight" );
  1509. // Compute intensity for each track in animation and apply
  1510. // Iterate animation tracks
  1511. for ( int i = 0; i < event->GetNumFlexAnimationTracks(); i++ )
  1512. {
  1513. CFlexAnimationTrack *track = event->GetFlexAnimationTrack( i );
  1514. if ( !track )
  1515. continue;
  1516. // Disabled
  1517. if ( !track->IsTrackActive() )
  1518. continue;
  1519. // If we are doing client side flexing, skip all tracks which are not server side
  1520. if ( g_bClientFlex && !track->IsServerSide() )
  1521. continue;
  1522. // Map track flex controller to global name
  1523. if ( track->IsComboType() )
  1524. {
  1525. for ( int side = 0; side < 2; side++ )
  1526. {
  1527. LocalFlexController_t controller = track->GetRawFlexControllerIndex( side );
  1528. // Get spline intensity for controller
  1529. float flIntensity = track->GetIntensity( scenetime, side );
  1530. if ( controller >= LocalFlexController_t(0) )
  1531. {
  1532. float orig = GetFlexWeight( controller );
  1533. SetFlexWeight( controller, orig * (1 - weight) + flIntensity * weight );
  1534. }
  1535. }
  1536. }
  1537. else
  1538. {
  1539. LocalFlexController_t controller = track->GetRawFlexControllerIndex( 0 );
  1540. // Get spline intensity for controller
  1541. float flIntensity = track->GetIntensity( scenetime, 0 );
  1542. if ( controller >= LocalFlexController_t(0) )
  1543. {
  1544. float orig = GetFlexWeight( controller );
  1545. SetFlexWeight( controller, orig * (1 - weight) + flIntensity * weight );
  1546. }
  1547. }
  1548. }
  1549. }
  1550. info->m_bStarted = true;
  1551. }
  1552. //-----------------------------------------------------------------------------
  1553. // Purpose:
  1554. // Input : *expr -
  1555. // scale -
  1556. // *pSettinghdr -
  1557. // newexpression -
  1558. //-----------------------------------------------------------------------------
  1559. void CBaseFlex::AddFlexSetting( const char *expr, float scale,
  1560. const flexsettinghdr_t *pSettinghdr, bool newexpression )
  1561. {
  1562. int i;
  1563. const flexsetting_t *pSetting = NULL;
  1564. // Find the named setting in the base
  1565. for ( i = 0; i < pSettinghdr->numflexsettings; i++ )
  1566. {
  1567. pSetting = pSettinghdr->pSetting( i );
  1568. if ( !pSetting )
  1569. continue;
  1570. const char *name = pSetting->pszName();
  1571. if ( !stricmp( name, expr ) )
  1572. break;
  1573. }
  1574. if ( i>=pSettinghdr->numflexsettings )
  1575. {
  1576. return;
  1577. }
  1578. flexweight_t *pWeights = NULL;
  1579. int truecount = pSetting->psetting( (byte *)pSettinghdr, 0, &pWeights );
  1580. if ( !pWeights )
  1581. return;
  1582. for (i = 0; i < truecount; i++, pWeights++)
  1583. {
  1584. // Translate to local flex controller
  1585. // this is translating from the settings's local index to the models local index
  1586. LocalFlexController_t index = FlexControllerLocalToGlobal( pSettinghdr, pWeights->key );
  1587. // blend scaled weighting in to total
  1588. float s = clamp( scale * pWeights->influence, 0.0f, 1.0f );
  1589. float value = GetFlexWeight( index ) * (1.0f - s ) + pWeights->weight * s;
  1590. SetFlexWeight( index, value );
  1591. }
  1592. }
  1593. //-----------------------------------------------------------------------------
  1594. // Purpose:
  1595. // Input : *actor -
  1596. // *parameters -
  1597. //-----------------------------------------------------------------------------
  1598. bool CBaseFlex::ProcessGestureSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event )
  1599. {
  1600. if ( !info || !event || !scene )
  1601. return false;
  1602. if ( info->m_iLayer == REQUEST_DEFERRED_LAYER_ALLOCATION )
  1603. {
  1604. HandleStartGestureSceneEvent( info, scene, event, info->m_pActor );
  1605. }
  1606. if (info->m_iLayer >= 0)
  1607. {
  1608. // this happens after StudioFrameAdvance()
  1609. // FIXME; this needs to be adjusted by npc offset to scene time? Known?
  1610. // FIXME: what should this do when the scene loops?
  1611. float duration = event->GetDuration( );
  1612. float flEventCycle = (scene->GetTime() - event->GetStartTime()) / duration;
  1613. float flCycle = event->GetOriginalPercentageFromPlaybackPercentage( flEventCycle );
  1614. SetLayerCycle( info->m_iLayer, flCycle );
  1615. float flWeight = event->GetIntensity( scene->GetTime() );
  1616. /*
  1617. if (stricmp( event->GetParameters(), "m_g_arms_crossed" ) == 0)
  1618. {
  1619. Msg("%.2f (%.2f) : %s : %.3f (%.3f) %.2f\n", scene->GetTime(), scene->GetTime() - event->GetStartTime(), event->GetParameters(), flCycle, flEventCycle, flWeight );
  1620. }
  1621. */
  1622. // fade out/in if npc is moving
  1623. if (!info->m_bIsGesture)
  1624. {
  1625. if (IsMoving())
  1626. {
  1627. info->m_flWeight = MAX( info->m_flWeight - 0.2, 0.0 );
  1628. }
  1629. else
  1630. {
  1631. info->m_flWeight = MIN( info->m_flWeight + 0.2, 1.0 );
  1632. }
  1633. }
  1634. // 3x^2-2x^3
  1635. float spline = 3 * info->m_flWeight * info->m_flWeight - 2 * info->m_flWeight * info->m_flWeight * info->m_flWeight;
  1636. SetLayerWeight( info->m_iLayer, flWeight * spline );
  1637. // update layer priority
  1638. if (m_bUpdateLayerPriorities)
  1639. {
  1640. SetLayerPriority( info->m_iLayer, info->m_iPriority + GetScenePriority( scene ) );
  1641. }
  1642. /*
  1643. Msg( "%d : %.2f (%.2f) : %.3f %.3f : %.3f\n",
  1644. info->m_iLayer,
  1645. scene->GetTime(),
  1646. (scene->GetTime() - event->GetStartTime()) / duration,
  1647. flCycle,
  1648. flNextCycle,
  1649. rate );
  1650. */
  1651. }
  1652. return true;
  1653. }
  1654. //-----------------------------------------------------------------------------
  1655. // Purpose:
  1656. // Input : *actor -
  1657. // *parameters -
  1658. //-----------------------------------------------------------------------------
  1659. bool CBaseFlex::ProcessSequenceSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event )
  1660. {
  1661. if ( !info || !event || !scene )
  1662. return false;
  1663. bool bNewlyAllocated = false;
  1664. if ( info->m_iLayer == REQUEST_DEFERRED_LAYER_ALLOCATION )
  1665. {
  1666. bool result = HandleStartSequenceSceneEvent( info, scene, event, info->m_pActor );
  1667. if (!result)
  1668. return false;
  1669. bNewlyAllocated = true;
  1670. }
  1671. if (info->m_iLayer >= 0)
  1672. {
  1673. float flWeight = event->GetIntensity( scene->GetTime() );
  1674. // force layer to zero weight in newly allocated, fixed bug with inter-think spawned sequences blending in badly
  1675. if (bNewlyAllocated)
  1676. flWeight = 0.0;
  1677. // fade out/in if npc is moving
  1678. bool bFadeOut = IsMoving();
  1679. CAI_BaseNPC *myNpc = MyNPCPointer( );
  1680. if (myNpc && !(myNpc->IsCurSchedule( SCHED_SCENE_GENERIC ) || myNpc->GetActivity() == ACT_IDLE_ANGRY || myNpc->GetActivity() == ACT_IDLE) )
  1681. {
  1682. bFadeOut = true;
  1683. if (info->m_flWeight == 1.0)
  1684. {
  1685. Warning( "%s playing CChoreoEvent::SEQUENCE but AI has forced them to do something different\n", STRING(GetEntityName()) );
  1686. }
  1687. }
  1688. if (bFadeOut)
  1689. {
  1690. info->m_flWeight = MAX( info->m_flWeight - 0.2, 0.0 );
  1691. }
  1692. else
  1693. {
  1694. info->m_flWeight = MIN( info->m_flWeight + 0.2, 1.0 );
  1695. }
  1696. float spline = 3 * info->m_flWeight * info->m_flWeight - 2 * info->m_flWeight * info->m_flWeight * info->m_flWeight;
  1697. SetLayerWeight( info->m_iLayer, flWeight * spline );
  1698. bool looping = ((GetSequenceFlags( GetModelPtr(), info->m_nSequence ) & STUDIO_LOOPING) != 0);
  1699. if (!looping)
  1700. {
  1701. float dt = scene->GetTime() - event->GetStartTime();
  1702. float seq_duration = SequenceDuration( info->m_nSequence );
  1703. float flCycle = dt / seq_duration;
  1704. flCycle = clamp( flCycle, 0, 1.0 );
  1705. SetLayerCycle( info->m_iLayer, flCycle );
  1706. }
  1707. if (myNpc)
  1708. {
  1709. myNpc->AddSceneLock( 0.2 );
  1710. }
  1711. // update layer priority
  1712. if (m_bUpdateLayerPriorities)
  1713. {
  1714. SetLayerPriority( info->m_iLayer, info->m_iPriority + GetScenePriority( scene ) );
  1715. }
  1716. }
  1717. // FIXME: clean up cycle index from restart
  1718. return true;
  1719. }
  1720. //-----------------------------------------------------------------------------
  1721. // Purpose: Returns true if the actor is not currently in a scene OR if the actor
  1722. // is in a scene (checked externally), but a PERMIT_RESPONSES event is active and
  1723. // the permit time period has enough time remaining to handle the response in full.
  1724. // Input : response_length -
  1725. //-----------------------------------------------------------------------------
  1726. bool CBaseFlex::PermitResponse( float response_length )
  1727. {
  1728. // Nothing set, disallow it
  1729. if ( m_flAllowResponsesEndTime <= 0.0f )
  1730. {
  1731. return false;
  1732. }
  1733. // If response ends before end of allow time, then that's okay
  1734. if ( gpGlobals->curtime + response_length <= m_flAllowResponsesEndTime )
  1735. {
  1736. return true;
  1737. }
  1738. // Disallow responses for now
  1739. return false;
  1740. }
  1741. //-----------------------------------------------------------------------------
  1742. // Purpose: Set response end time (0 to clear response blocking)
  1743. // Input : endtime -
  1744. //-----------------------------------------------------------------------------
  1745. void CBaseFlex::SetPermitResponse( float endtime )
  1746. {
  1747. // Mark time after which we'll be occupied again (if in a scene)
  1748. // Note a value of <= 0.0f means unset (always allow actor to speak if not in a scene,
  1749. // and always disallow if in a scene)
  1750. m_flAllowResponsesEndTime = endtime;
  1751. }
  1752. //-----------------------------------------------------------------------------
  1753. // Purpose: Play a one-shot scene
  1754. // Input :
  1755. // Output :
  1756. //-----------------------------------------------------------------------------
  1757. float CBaseFlex::PlayScene( const char *pszScene, float flDelay, AI_Response *response, IRecipientFilter *filter /* = NULL */ )
  1758. {
  1759. return InstancedScriptedScene( this, pszScene, NULL, flDelay, false, response, false, filter );
  1760. }
  1761. float CBaseFlex::ScriptPlayScene( const char* pszScene, float flDelay )
  1762. {
  1763. return InstancedScriptedScene( this, pszScene, NULL, flDelay );
  1764. }
  1765. //-----------------------------------------------------------------------------
  1766. // Purpose: Generate a one-shot scene in memory with one track which is to play the named sound on the actor
  1767. // Input : *soundname -
  1768. // Output : float
  1769. //-----------------------------------------------------------------------------
  1770. float CBaseFlex::PlayAutoGeneratedSoundScene( const char *soundname )
  1771. {
  1772. return InstancedAutoGeneratedSoundScene( this, soundname );
  1773. }
  1774. //--------------------------------------------------------------------------------------------------
  1775. // Returns the script instance of the scene entity associated with our oldest ("top level") scene event
  1776. //--------------------------------------------------------------------------------------------------
  1777. HSCRIPT CBaseFlex::ScriptGetOldestScene( void )
  1778. {
  1779. if ( m_SceneEvents.Count() > 0 )
  1780. {
  1781. CSceneEventInfo curScene = m_SceneEvents.Head();
  1782. return ToHScript( (CBaseEntity*)(curScene.m_hSceneEntity.Get()) );
  1783. }
  1784. else
  1785. {
  1786. return NULL;
  1787. }
  1788. }
  1789. //--------------------------------------------------------------------------------------------------
  1790. // Returns the script instance of the scene at the specified index, or null if index >= count
  1791. //--------------------------------------------------------------------------------------------------
  1792. HSCRIPT CBaseFlex::ScriptGetSceneByIndex( int index )
  1793. {
  1794. if ( m_SceneEvents.IsValidIndex( index ) )
  1795. {
  1796. CSceneEventInfo curScene = m_SceneEvents.Element( index );
  1797. return ToHScript( (CBaseEntity*)(curScene.m_hSceneEntity.Get()) );
  1798. }
  1799. else
  1800. {
  1801. return NULL;
  1802. }
  1803. }
  1804. // FIXME: move to CBaseActor
  1805. bool CBaseFlex::EnterSceneSequence( CChoreoScene *scene, CChoreoEvent *event, bool bRestart )
  1806. {
  1807. CAI_BaseNPC *myNpc = MyNPCPointer( );
  1808. if (!myNpc)
  1809. return false;
  1810. // 2 seconds past current event, or 0.2 seconds past end of scene, whichever is shorter
  1811. float flDuration = MIN( 2.0, MIN( event->GetEndTime() - scene->GetTime() + 2.0, scene->FindStopTime() - scene->GetTime() + 0.2 ) );
  1812. if (myNpc->IsCurSchedule( SCHED_SCENE_GENERIC ))
  1813. {
  1814. myNpc->AddSceneLock( flDuration );
  1815. return true;
  1816. }
  1817. // for now, don't interrupt sequences that don't understand being interrupted
  1818. if (myNpc->GetCurSchedule())
  1819. {
  1820. CAI_ScheduleBits testBits;
  1821. myNpc->GetCurSchedule()->GetInterruptMask( &testBits );
  1822. testBits.Clear( COND_PROVOKED );
  1823. if (testBits.IsAllClear())
  1824. {
  1825. return false;
  1826. }
  1827. }
  1828. if (myNpc->IsInterruptable())
  1829. {
  1830. if (myNpc->m_hCine)
  1831. {
  1832. // Assert( !(myNpc->GetFlags() & FL_FLY ) );
  1833. myNpc->ExitScriptedSequence( );
  1834. }
  1835. myNpc->OnStartScene();
  1836. myNpc->SetSchedule( SCHED_SCENE_GENERIC );
  1837. myNpc->AddSceneLock( flDuration );
  1838. return true;
  1839. }
  1840. return false;
  1841. }
  1842. bool CBaseFlex::ExitSceneSequence( void )
  1843. {
  1844. return true;
  1845. }
  1846. //-----------------------------------------------------------------------------
  1847. // Purpose: keep track of last valid flex animation time and returns if the current info should play theirs
  1848. //-----------------------------------------------------------------------------
  1849. bool CBaseFlex::IsSuppressedFlexAnimation( CSceneEventInfo *info )
  1850. {
  1851. // check for suppression if the current info is a background
  1852. if (info->m_pScene && info->m_pScene->IsBackground())
  1853. {
  1854. // allow for slight jitter
  1855. return m_flLastFlexAnimationTime > gpGlobals->curtime - GetAnimTimeInterval() * 1.5;
  1856. }
  1857. // keep track of last non-suppressable flex animation
  1858. m_flLastFlexAnimationTime = gpGlobals->curtime;
  1859. return false;
  1860. }
  1861. //-----------------------------------------------------------------------------
  1862. // Purpose: Clear out body lean states that are invalidated with Teleport
  1863. //-----------------------------------------------------------------------------
  1864. void CBaseFlex::Teleport( const Vector *newPosition, const QAngle *newAngles, const Vector *newVelocity, bool bUseSlowHighAccuracyContacts )
  1865. {
  1866. BaseClass::Teleport( newPosition, newAngles, newVelocity, bUseSlowHighAccuracyContacts );
  1867. #ifdef HL2_DLL
  1868. // clear out Body Lean
  1869. m_vecPrevOrigin = vec3_origin;
  1870. #endif
  1871. }
  1872. //-----------------------------------------------------------------------------
  1873. // Purpose: keep track of accel/decal and lean the body
  1874. //-----------------------------------------------------------------------------
  1875. void CBaseFlex::DoBodyLean( void )
  1876. {
  1877. #ifdef HL2_DLL
  1878. CAI_BaseNPC *myNpc = MyNPCPointer( );
  1879. if (myNpc)
  1880. {
  1881. Vector vecDelta;
  1882. Vector vecPos;
  1883. Vector vecOrigin = GetAbsOrigin();
  1884. if (m_vecPrevOrigin == vec3_origin)
  1885. {
  1886. m_vecPrevOrigin = vecOrigin;
  1887. }
  1888. vecDelta = vecOrigin - m_vecPrevOrigin;
  1889. vecDelta.x = clamp( vecDelta.x, -50, 50 );
  1890. vecDelta.y = clamp( vecDelta.y, -50, 50 );
  1891. vecDelta.z = clamp( vecDelta.z, -50, 50 );
  1892. float dt = gpGlobals->curtime - GetLastThink();
  1893. bool bSkip = ((GetFlags() & (FL_FLY | FL_SWIM)) != 0) || (GetMoveParent() != NULL) || (GetGroundEntity() == NULL) || (GetGroundEntity()->IsMoving());
  1894. bSkip |= myNpc->TaskRanAutomovement() || (myNpc->GetVehicleEntity() != NULL);
  1895. if (!bSkip)
  1896. {
  1897. if (vecDelta.LengthSqr() > m_vecPrevVelocity.LengthSqr())
  1898. {
  1899. float decay = ExponentialDecay( 0.6, 0.1, dt );
  1900. m_vecPrevVelocity = m_vecPrevVelocity * (decay) + vecDelta * (1.f - decay);
  1901. }
  1902. else
  1903. {
  1904. float decay = ExponentialDecay( 0.4, 0.1, dt );
  1905. m_vecPrevVelocity = m_vecPrevVelocity * (decay) + vecDelta * (1.f - decay);
  1906. }
  1907. vecPos = m_vecPrevOrigin + m_vecPrevVelocity;
  1908. float decay = ExponentialDecay( 0.5, 0.1, dt );
  1909. m_vecShift = m_vecShift * (decay) + (vecOrigin - vecPos) * (1.f - decay); // FIXME: Scale this
  1910. m_vecLean = (vecOrigin - vecPos) * 1.0; // FIXME: Scale this
  1911. }
  1912. else
  1913. {
  1914. m_vecPrevVelocity = vecDelta;
  1915. float decay = ExponentialDecay( 0.5, 0.1, dt );
  1916. m_vecShift = m_vecShift * decay;
  1917. m_vecLean = m_vecLean * decay;
  1918. }
  1919. m_vecPrevOrigin = vecOrigin;
  1920. /*
  1921. DevMsg( "%.2f %.2f %.2f (%.2f %.2f %.2f)\n",
  1922. m_vecLean.Get().x, m_vecLean.Get().y, m_vecLean.Get().z,
  1923. vecDelta.x, vecDelta.y, vecDelta.z );
  1924. */
  1925. }
  1926. #endif
  1927. }
  1928. //-----------------------------------------------------------------------------
  1929. // Purpose: initialize weight for background events
  1930. //-----------------------------------------------------------------------------
  1931. void CSceneEventInfo::InitWeight( CBaseFlex *pActor )
  1932. {
  1933. if (pActor->IsSuppressedFlexAnimation( this ))
  1934. {
  1935. m_flWeight = 0.0;
  1936. }
  1937. else
  1938. {
  1939. m_flWeight = 1.0;
  1940. }
  1941. }
  1942. //-----------------------------------------------------------------------------
  1943. // Purpose: update weight for background events. Only call once per think
  1944. //-----------------------------------------------------------------------------
  1945. float CSceneEventInfo::UpdateWeight( CBaseFlex *pActor )
  1946. {
  1947. // decay if this is a background scene and there's other flex animations playing
  1948. if (pActor->IsSuppressedFlexAnimation( this ))
  1949. {
  1950. m_flWeight = MAX( m_flWeight - 0.2, 0.0 );
  1951. }
  1952. else
  1953. {
  1954. m_flWeight = MIN( m_flWeight + 0.1, 1.0 );
  1955. }
  1956. return m_flWeight;
  1957. }
  1958. BEGIN_DATADESC( CFlexCycler )
  1959. DEFINE_FIELD( m_flextime, FIELD_TIME ),
  1960. DEFINE_FIELD( m_flexnum, FIELD_INTEGER ),
  1961. DEFINE_ARRAY( m_flextarget, FIELD_FLOAT, 64 ),
  1962. DEFINE_FIELD( m_blinktime, FIELD_TIME ),
  1963. DEFINE_FIELD( m_looktime, FIELD_TIME ),
  1964. DEFINE_FIELD( m_lookTarget, FIELD_POSITION_VECTOR ),
  1965. DEFINE_FIELD( m_speaktime, FIELD_TIME ),
  1966. DEFINE_FIELD( m_istalking, FIELD_INTEGER ),
  1967. DEFINE_FIELD( m_phoneme, FIELD_INTEGER ),
  1968. DEFINE_KEYFIELD( m_iszSentence, FIELD_STRING, "Sentence" ),
  1969. DEFINE_FIELD( m_sentence, FIELD_INTEGER ),
  1970. END_DATADESC()
  1971. LINK_ENTITY_TO_CLASS( cycler_flex, CGenericFlexCycler );
  1972. ConVar flex_expression( "flex_expression","-" );
  1973. ConVar flex_talk( "flex_talk","0" );
  1974. // Cycler member functions
  1975. void CFlexCycler::GenericCyclerSpawn(char *szModel, Vector vecMin, Vector vecMax)
  1976. {
  1977. if (!szModel || !*szModel)
  1978. {
  1979. Warning( "cycler at %.0f %.0f %0.f missing modelname\n", GetAbsOrigin().x, GetAbsOrigin().y, GetAbsOrigin().z );
  1980. UTIL_Remove( this );
  1981. return;
  1982. }
  1983. PrecacheModel( szModel );
  1984. SetModel( szModel );
  1985. CFlexCycler::Spawn( );
  1986. UTIL_SetSize(this, vecMin, vecMax);
  1987. Vector vecEyeOffset;
  1988. GetEyePosition( GetModelPtr(), vecEyeOffset );
  1989. SetViewOffset( vecEyeOffset );
  1990. InitBoneControllers();
  1991. if (GetNumFlexControllers() < 5)
  1992. Warning( "cycler_flex used on model %s without enough flexes.\n", szModel );
  1993. }
  1994. void CFlexCycler::Spawn( )
  1995. {
  1996. Precache();
  1997. /*
  1998. if ( m_spawnflags & FCYCLER_NOTSOLID )
  1999. {
  2000. SetSolid( SOLID_NOT );
  2001. }
  2002. else
  2003. {
  2004. SetSolid( SOLID_SLIDEBOX );
  2005. }
  2006. */
  2007. SetSolid( SOLID_BBOX );
  2008. AddSolidFlags( FSOLID_NOT_STANDABLE );
  2009. SetMoveType( MOVETYPE_NONE );
  2010. m_takedamage = DAMAGE_YES;
  2011. m_iHealth = 80000;// no cycler should die
  2012. m_flPlaybackRate = 1.0f;
  2013. m_flGroundSpeed = 0;
  2014. SetNextThink( gpGlobals->curtime + 1.0f );
  2015. ResetSequenceInfo( );
  2016. m_flCycle = random->RandomFloat( 0, 1.0 );
  2017. }
  2018. const char *predef_flexcontroller_names[] = {
  2019. "right_lid_raiser",
  2020. "left_lid_raiser",
  2021. "right_lid_tightener",
  2022. "left_lid_tightener",
  2023. "right_lid_droop",
  2024. "left_lid_droop",
  2025. "right_inner_raiser",
  2026. "left_inner_raiser",
  2027. "right_outer_raiser",
  2028. "left_outer_raiser",
  2029. "right_lowerer",
  2030. "left_lowerer",
  2031. "right_cheek_raiser",
  2032. "left_cheek_raiser",
  2033. "wrinkler",
  2034. "right_upper_raiser",
  2035. "left_upper_raiser",
  2036. "right_corner_puller",
  2037. "left_corner_puller",
  2038. "corner_depressor",
  2039. "chin_raiser",
  2040. "right_puckerer",
  2041. "left_puckerer",
  2042. "right_funneler",
  2043. "left_funneler",
  2044. "tightener",
  2045. "jaw_clencher",
  2046. "jaw_drop",
  2047. "right_mouth_drop",
  2048. "left_mouth_drop",
  2049. NULL };
  2050. float predef_flexcontroller_values[7][30] = {
  2051. /* 0 */ { 0.700,0.560,0.650,0.650,0.650,0.585,0.000,0.000,0.400,0.040,0.000,0.000,0.450,0.450,0.000,0.000,0.000,0.750,0.000,0.000,0.000,0.000,0.000,0.000,0.000,0.150,1.000,0.000,0.000,0.000 },
  2052. /* 1 */ { 0.450,0.450,0.450,0.450,0.000,0.000,0.000,0.000,0.300,0.300,0.000,0.000,0.250,0.250,0.000,0.000,0.000,0.750,0.750,0.000,0.000,0.000,0.000,0.400,0.400,0.000,1.000,0.000,0.050,0.050 },
  2053. /* 2 */ { 0.200,0.200,0.500,0.500,0.150,0.150,0.100,0.100,0.150,0.150,0.000,0.000,0.700,0.700,0.000,0.000,0.000,0.750,0.750,0.000,0.200,0.000,0.000,0.000,0.000,0.000,0.850,0.000,0.000,0.000 },
  2054. /* 3 */ { 0.000,0.000,0.000,0.000,0.000,0.000,0.300,0.300,0.000,0.000,0.000,0.000,0.000,0.000,0.100,0.000,0.000,0.000,0.000,0.700,0.300,0.000,0.000,0.200,0.200,0.000,0.000,0.300,0.000,0.000 },
  2055. /* 4 */ { 0.450,0.450,0.000,0.000,0.450,0.450,0.000,0.000,0.000,0.000,0.450,0.450,0.000,0.000,0.000,0.000,0.000,0.000,0.000,0.000,0.000,0.000,0.000,0.000,0.000,0.300,0.000,0.000,0.000,0.000 },
  2056. /* 5 */ { 0.000,0.000,0.350,0.350,0.150,0.150,0.300,0.300,0.450,0.450,0.000,0.000,0.200,0.200,0.000,0.000,0.000,0.000,0.000,0.000,0.000,0.200,0.200,0.000,0.000,0.300,0.000,0.000,0.000,0.000 },
  2057. /* 6 */ { 0.000,0.000,0.650,0.650,0.750,0.750,0.000,0.000,0.000,0.000,0.300,0.300,0.000,0.000,0.000,0.250,0.250,0.000,0.000,0.000,0.000,0.000,0.000,0.000,0.000,0.000,0.000,0.000,0.000,0.000 }
  2058. };
  2059. //-----------------------------------------------------------------------------
  2060. // Purpose: Changes sequences when shot
  2061. //-----------------------------------------------------------------------------
  2062. int CFlexCycler::OnTakeDamage( const CTakeDamageInfo &info )
  2063. {
  2064. int nSequence = GetSequence() + 1;
  2065. if (!IsValidSequence( nSequence ))
  2066. {
  2067. nSequence = 0;
  2068. }
  2069. ResetSequence( nSequence );
  2070. m_flCycle = 0;
  2071. return 0;
  2072. }
  2073. void CFlexCycler::SetFlexTarget( LocalFlexController_t flexnum )
  2074. {
  2075. m_flextarget[flexnum] = random->RandomFloat( 0.5, 1.0 );
  2076. const char *pszType = GetFlexControllerType( flexnum );
  2077. // zero out all other flexes of the same type
  2078. for (LocalFlexController_t i = LocalFlexController_t(0); i < GetNumFlexControllers(); i++)
  2079. {
  2080. if (i != flexnum)
  2081. {
  2082. const char *pszOtherType = GetFlexControllerType( i );
  2083. if (stricmp( pszType, pszOtherType ) == 0)
  2084. {
  2085. m_flextarget[i] = 0;
  2086. }
  2087. }
  2088. }
  2089. // HACK, for now, consider then linked is named "right_" or "left_"
  2090. if ( StringHasPrefixCaseSensitive( GetFlexControllerName( flexnum ), "right_" ) )
  2091. {
  2092. m_flextarget[flexnum+1] = m_flextarget[flexnum];
  2093. }
  2094. else if ( StringHasPrefixCaseSensitive( GetFlexControllerName( flexnum ), "left_" ) )
  2095. {
  2096. m_flextarget[flexnum-1] = m_flextarget[flexnum];
  2097. }
  2098. }
  2099. LocalFlexController_t CFlexCycler::LookupFlex( const char *szTarget )
  2100. {
  2101. for (LocalFlexController_t i = LocalFlexController_t(0); i < GetNumFlexControllers(); i++)
  2102. {
  2103. const char *pszFlex = GetFlexControllerName( i );
  2104. if (stricmp( szTarget, pszFlex ) == 0)
  2105. {
  2106. return i;
  2107. }
  2108. }
  2109. return LocalFlexController_t(-1);
  2110. }
  2111. void CFlexCycler::Think( void )
  2112. {
  2113. SetNextThink( gpGlobals->curtime + 0.1f );
  2114. StudioFrameAdvance ( );
  2115. if (IsSequenceFinished() && !SequenceLoops())
  2116. {
  2117. // ResetSequenceInfo();
  2118. // hack to avoid reloading model every frame
  2119. m_flAnimTime = gpGlobals->curtime;
  2120. m_flPlaybackRate = 1.0;
  2121. m_bSequenceFinished = false;
  2122. m_flLastEventCheck = 0;
  2123. m_flCycle = 0;
  2124. }
  2125. // only do this if they have more than eyelid movement
  2126. if (GetNumFlexControllers() > 2)
  2127. {
  2128. const char *pszExpression = flex_expression.GetString();
  2129. if (pszExpression && pszExpression[0] == '+' && pszExpression[1] != '\0')
  2130. {
  2131. int i;
  2132. int j = atoi( &pszExpression[1] );
  2133. for ( i = 0; i < GetNumFlexControllers(); i++)
  2134. {
  2135. m_flextarget[m_flexnum] = 0;
  2136. }
  2137. for (i = 0; i < 35 && predef_flexcontroller_names[i]; i++)
  2138. {
  2139. m_flexnum = LookupFlex( predef_flexcontroller_names[i] );
  2140. m_flextarget[m_flexnum] = predef_flexcontroller_values[j][i];
  2141. // Msg( "%s %.3f\n", predef_flexcontroller_names[i], predef_flexcontroller_values[j][i] );
  2142. }
  2143. }
  2144. else if ( pszExpression && (pszExpression[0] == '1') && (pszExpression[1] == '\0') ) // 1 for maxed controller values
  2145. {
  2146. for ( LocalFlexController_t i = LocalFlexController_t(0); i < GetNumFlexControllers(); i++ )
  2147. {
  2148. // Max everything out...
  2149. m_flextarget[i] = 1.0f;
  2150. SetFlexWeight( i, m_flextarget[i] );
  2151. }
  2152. }
  2153. else if ( pszExpression && (pszExpression[0] == '^') && (pszExpression[1] == '\0') ) // ^ for sine wave
  2154. {
  2155. for ( LocalFlexController_t i = LocalFlexController_t(0); i < GetNumFlexControllers(); i++ )
  2156. {
  2157. // Throw a differently offset sine wave on all of the flex controllers
  2158. float fFlexTime = i * (1.0f / (float)GetNumFlexControllers()) + gpGlobals->curtime;
  2159. m_flextarget[i] = sinf( fFlexTime ) * 0.5f + 0.5f;
  2160. SetFlexWeight( i, m_flextarget[i] );
  2161. }
  2162. }
  2163. else if (pszExpression && pszExpression[0] != '\0' && strcmp(pszExpression, "+") != 0)
  2164. {
  2165. char szExpression[128];
  2166. char szTemp[32];
  2167. Q_strncpy( szExpression, pszExpression ,sizeof(szExpression));
  2168. char *pszExpression = szExpression;
  2169. while (*pszExpression != '\0')
  2170. {
  2171. if (*pszExpression == '+')
  2172. *pszExpression = ' ';
  2173. pszExpression++;
  2174. }
  2175. pszExpression = szExpression;
  2176. while (*pszExpression)
  2177. {
  2178. if (*pszExpression != ' ')
  2179. {
  2180. if (*pszExpression == '-')
  2181. {
  2182. for (LocalFlexController_t i = LocalFlexController_t(0); i < GetNumFlexControllers(); i++)
  2183. {
  2184. m_flextarget[i] = 0;
  2185. }
  2186. }
  2187. else if (*pszExpression == '?')
  2188. {
  2189. for (LocalFlexController_t i = LocalFlexController_t(0); i < GetNumFlexControllers(); i++)
  2190. {
  2191. Msg( "\"%s\" ", GetFlexControllerName( i ) );
  2192. }
  2193. Msg( "\n" );
  2194. flex_expression.SetValue( "" );
  2195. }
  2196. else
  2197. {
  2198. if (sscanf( pszExpression, "%31s", szTemp ) == 1)
  2199. {
  2200. m_flexnum = LookupFlex( szTemp );
  2201. if (m_flexnum != -1 && m_flextarget[m_flexnum] != 1)
  2202. {
  2203. m_flextarget[m_flexnum] = 1.0;
  2204. // SetFlexTarget( m_flexnum );
  2205. }
  2206. pszExpression += strlen( szTemp ) - 1;
  2207. }
  2208. }
  2209. }
  2210. pszExpression++;
  2211. }
  2212. }
  2213. else if (m_flextime < gpGlobals->curtime)
  2214. {
  2215. // m_flextime = gpGlobals->curtime + 1.0; // RandomFloat( 0.1, 0.5 );
  2216. m_flextime = gpGlobals->curtime + random->RandomFloat( 0.3, 0.5 ) * (30.0 / GetNumFlexControllers());
  2217. m_flexnum = (LocalFlexController_t)random->RandomInt( 0, GetNumFlexControllers() - 1 );
  2218. // m_flexnum = (pflex->num + 1) % r_psubmodel->numflexes;
  2219. if (m_flextarget[m_flexnum] == 1)
  2220. {
  2221. m_flextarget[m_flexnum] = 0;
  2222. // pflex->time = cl.time + 0.1;
  2223. }
  2224. else if (stricmp( GetFlexControllerType( m_flexnum ), "phoneme" ) != 0)
  2225. {
  2226. if (strstr( GetFlexControllerName( m_flexnum ), "upper_raiser" ) == NULL)
  2227. {
  2228. Msg( "%s:%s\n", GetFlexControllerType( m_flexnum ), GetFlexControllerName( m_flexnum ) );
  2229. SetFlexTarget( m_flexnum );
  2230. }
  2231. }
  2232. #if 0
  2233. char szWhat[256];
  2234. szWhat[0] = '\0';
  2235. for (int i = 0; i < GetNumFlexControllers(); i++)
  2236. {
  2237. if (m_flextarget[i] == 1.0)
  2238. {
  2239. if (stricmp( GetFlexFacs( i ), "upper") != 0 && stricmp( GetFlexFacs( i ), "lower") != 0)
  2240. {
  2241. if (szWhat[0] == '\0')
  2242. Q_strncat( szWhat, "-", sizeof( szWhat ), COPY_ALL_CHARACTERS );
  2243. else
  2244. Q_strncat( szWhat, "+", sizeof( szWhat ), COPY_ALL_CHARACTERS );
  2245. Q_strncat( szWhat, GetFlexFacs( i ), sizeof( szWhat ), COPY_ALL_CHARACTERS );
  2246. }
  2247. }
  2248. }
  2249. Msg( "%s\n", szWhat );
  2250. #endif
  2251. }
  2252. // slide it up.
  2253. for (LocalFlexController_t i = LocalFlexController_t(0); i < GetNumFlexControllers(); i++)
  2254. {
  2255. float weight = GetFlexWeight( i );
  2256. if (weight != m_flextarget[i])
  2257. {
  2258. weight = weight + (m_flextarget[i] - weight) / random->RandomFloat( 2.0, 4.0 );
  2259. }
  2260. weight = clamp( weight, 0.0f, 1.0f );
  2261. SetFlexWeight( i, weight );
  2262. }
  2263. #if 1
  2264. if (flex_talk.GetInt() == -1)
  2265. {
  2266. m_istalking = 1;
  2267. char pszSentence[256];
  2268. Q_snprintf( pszSentence,sizeof(pszSentence), "%s%d", STRING(m_iszSentence), m_sentence++ );
  2269. int sentenceIndex = engine->SentenceIndexFromName( pszSentence );
  2270. if (sentenceIndex >= 0)
  2271. {
  2272. Msg( "%d : %s\n", sentenceIndex, pszSentence );
  2273. CPASAttenuationFilter filter( this );
  2274. CBaseEntity::EmitSentenceByIndex( filter, entindex(), CHAN_VOICE, sentenceIndex, 1, SNDLVL_TALKING, 0, PITCH_NORM );
  2275. }
  2276. else
  2277. {
  2278. m_sentence = 0;
  2279. }
  2280. flex_talk.SetValue( "0" );
  2281. }
  2282. else if (!FStrEq( flex_talk.GetString(), "0") )
  2283. {
  2284. int sentenceIndex = engine->SentenceIndexFromName( flex_talk.GetString() );
  2285. if (sentenceIndex >= 0)
  2286. {
  2287. CPASAttenuationFilter filter( this );
  2288. CBaseEntity::EmitSentenceByIndex( filter, entindex(), CHAN_VOICE, sentenceIndex, 1, SNDLVL_TALKING, 0, PITCH_NORM );
  2289. }
  2290. flex_talk.SetValue( "0" );
  2291. }
  2292. #else
  2293. if (flex_talk.GetInt())
  2294. {
  2295. if (m_speaktime < gpGlobals->curtime)
  2296. {
  2297. if (m_phoneme == 0)
  2298. {
  2299. for (m_phoneme = 0; m_phoneme < GetNumFlexControllers(); m_phoneme++)
  2300. {
  2301. if (stricmp( GetFlexFacs( m_phoneme ), "27") == 0)
  2302. break;
  2303. }
  2304. }
  2305. m_istalking = !m_istalking;
  2306. if (m_istalking)
  2307. {
  2308. m_looktime = gpGlobals->curtime - 1.0;
  2309. m_speaktime = gpGlobals->curtime + random->RandomFloat( 0.5, 2.0 );
  2310. }
  2311. else
  2312. {
  2313. m_speaktime = gpGlobals->curtime + random->RandomFloat( 1.0, 3.0 );
  2314. }
  2315. }
  2316. for (i = m_phoneme; i < GetNumFlexControllers(); i++)
  2317. {
  2318. SetFlexWeight( i, 0.0f );
  2319. }
  2320. if (m_istalking)
  2321. {
  2322. m_flextime = gpGlobals->curtime + random->RandomFloat( 0.0, 0.2 );
  2323. m_flexWeight[random->RandomInt(m_phoneme, GetNumFlexControllers()-1)] = random->RandomFloat( 0.5, 1.0 );
  2324. float mouth = random->RandomFloat( 0.0, 1.0 );
  2325. float jaw = random->RandomFloat( 0.0, 1.0 );
  2326. m_flexWeight[m_phoneme - 2] = jaw * (mouth);
  2327. m_flexWeight[m_phoneme - 1] = jaw * (1.0 - mouth);
  2328. }
  2329. }
  2330. else
  2331. {
  2332. m_istalking = 0;
  2333. }
  2334. #endif
  2335. // blink
  2336. if (m_blinktime < gpGlobals->curtime)
  2337. {
  2338. Blink();
  2339. m_blinktime = gpGlobals->curtime + random->RandomFloat( 1.5, 4.5 );
  2340. }
  2341. }
  2342. Vector forward, right, up;
  2343. GetVectors( &forward, &right, &up );
  2344. CBaseEntity *pPlayer = (CBaseEntity *)UTIL_GetLocalPlayer();
  2345. if (pPlayer)
  2346. {
  2347. if (pPlayer->GetSmoothedVelocity().Length() != 0 && DotProduct( forward, pPlayer->EyePosition() - EyePosition()) > 0.5)
  2348. {
  2349. m_lookTarget = pPlayer->EyePosition();
  2350. m_looktime = gpGlobals->curtime + random->RandomFloat(2.0,4.0);
  2351. }
  2352. else if (m_looktime < gpGlobals->curtime)
  2353. {
  2354. if ((!m_istalking) && random->RandomInt( 0, 1 ) == 0)
  2355. {
  2356. m_lookTarget = EyePosition() + forward * 128 + right * random->RandomFloat(-64,64) + up * random->RandomFloat(-32,32);
  2357. m_looktime = gpGlobals->curtime + random->RandomFloat(0.3,1.0);
  2358. if (m_blinktime - 0.5 < gpGlobals->curtime)
  2359. {
  2360. Blink();
  2361. }
  2362. }
  2363. else
  2364. {
  2365. m_lookTarget = pPlayer->EyePosition();
  2366. m_looktime = gpGlobals->curtime + random->RandomFloat(1.0,4.0);
  2367. }
  2368. }
  2369. #if 0
  2370. float dt = acos( DotProduct( (m_lookTarget - EyePosition()).Normalize(), (m_viewtarget - EyePosition()).Normalize() ) );
  2371. if (dt > M_PI / 4)
  2372. {
  2373. dt = (M_PI / 4) * dt;
  2374. m_viewtarget = ((1 - dt) * m_viewtarget + dt * m_lookTarget);
  2375. }
  2376. #endif
  2377. SetViewtarget( m_lookTarget );
  2378. }
  2379. // Handle any facial animation from scene playback
  2380. // FIXME: do we still actually need flex cyclers?
  2381. // AddSceneSceneEvents();
  2382. }
  2383. void CFlexCycler::ProcessSceneEvents( void )
  2384. {
  2385. // Don't do anything since we handle facial stuff in Think()
  2386. }
  2387. BEGIN_BYTESWAP_DATADESC( flexsettinghdr_t )
  2388. DEFINE_FIELD( id, FIELD_INTEGER ),
  2389. DEFINE_FIELD( version, FIELD_INTEGER ),
  2390. DEFINE_ARRAY( name, FIELD_CHARACTER, 64 ),
  2391. DEFINE_FIELD( length, FIELD_INTEGER ),
  2392. DEFINE_FIELD( numflexsettings, FIELD_INTEGER ),
  2393. DEFINE_FIELD( flexsettingindex, FIELD_INTEGER ),
  2394. DEFINE_FIELD( nameindex, FIELD_INTEGER ),
  2395. DEFINE_FIELD( numindexes, FIELD_INTEGER ),
  2396. DEFINE_FIELD( indexindex, FIELD_INTEGER ),
  2397. DEFINE_FIELD( numkeys, FIELD_INTEGER ),
  2398. DEFINE_FIELD( keynameindex, FIELD_INTEGER ),
  2399. DEFINE_FIELD( keymappingindex, FIELD_INTEGER ),
  2400. END_BYTESWAP_DATADESC()
  2401. BEGIN_BYTESWAP_DATADESC( flexsetting_t )
  2402. DEFINE_FIELD( nameindex, FIELD_INTEGER ),
  2403. DEFINE_FIELD( obsolete1, FIELD_INTEGER ),
  2404. DEFINE_FIELD( numsettings, FIELD_INTEGER ),
  2405. DEFINE_FIELD( index, FIELD_INTEGER ),
  2406. DEFINE_FIELD( obsolete2, FIELD_INTEGER ),
  2407. DEFINE_FIELD( settingindex, FIELD_INTEGER ),
  2408. END_BYTESWAP_DATADESC()
  2409. BEGIN_BYTESWAP_DATADESC( flexweight_t )
  2410. DEFINE_FIELD( key, FIELD_INTEGER ),
  2411. DEFINE_FIELD( weight, FIELD_FLOAT ),
  2412. DEFINE_FIELD( influence, FIELD_FLOAT ),
  2413. END_BYTESWAP_DATADESC()