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

1038 lines
26 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. #include "cbase.h"
  9. #include "studio.h"
  10. #include "activitylist.h"
  11. #include "engine/IEngineSound.h"
  12. #include "ai_activity.h"
  13. #include "animation.h"
  14. #include "bone_setup.h"
  15. #include "scriptevent.h"
  16. #include "npcevent.h"
  17. #include "eventlist.h"
  18. #include "tier0/vprof.h"
  19. #if !defined( CLIENT_DLL ) && !defined( MAKEXVCD )
  20. #include "util.h"
  21. #include "enginecallback.h"
  22. #endif
  23. // memdbgon must be the last include file in a .cpp file!!!
  24. #include "tier0/memdbgon.h"
  25. #pragma warning( disable : 4244 )
  26. #define iabs(i) (( (i) >= 0 ) ? (i) : -(i) )
  27. int ExtractBbox( CStudioHdr *pstudiohdr, int sequence, Vector& mins, Vector& maxs )
  28. {
  29. if (! pstudiohdr)
  30. return 0;
  31. if (!pstudiohdr->SequencesAvailable())
  32. return 0;
  33. mstudioseqdesc_t &seqdesc = pstudiohdr->pSeqdesc( sequence );
  34. mins = seqdesc.bbmin;
  35. maxs = seqdesc.bbmax;
  36. return 1;
  37. }
  38. //-----------------------------------------------------------------------------
  39. // Purpose:
  40. //
  41. // Input : *pstudiohdr -
  42. // iSequence -
  43. //
  44. // Output : mstudioseqdesc_t
  45. //-----------------------------------------------------------------------------
  46. extern int g_nActivityListVersion;
  47. extern int g_nEventListVersion;
  48. void SetEventIndexForSequence( mstudioseqdesc_t &seqdesc )
  49. {
  50. if ( &seqdesc == NULL )
  51. return;
  52. seqdesc.flags |= STUDIO_EVENT;
  53. if ( seqdesc.numevents == 0 )
  54. return;
  55. for ( int index = 0; index < (int)seqdesc.numevents; index++ )
  56. {
  57. mstudioevent_t *pevent = seqdesc.pEvent( index );
  58. if ( !pevent )
  59. continue;
  60. if ( pevent->type & AE_TYPE_NEWEVENTSYSTEM )
  61. {
  62. const char *pEventName = pevent->pszEventName();
  63. int iEventIndex = EventList_IndexForName( pEventName );
  64. if ( iEventIndex == -1 )
  65. {
  66. pevent->event = EventList_RegisterPrivateEvent( pEventName );
  67. }
  68. else
  69. {
  70. pevent->event = iEventIndex;
  71. pevent->type |= EventList_GetEventType( iEventIndex );
  72. }
  73. }
  74. }
  75. }
  76. mstudioevent_t *GetEventIndexForSequence( mstudioseqdesc_t &seqdesc )
  77. {
  78. if (!(seqdesc.flags & STUDIO_EVENT))
  79. {
  80. SetEventIndexForSequence( seqdesc );
  81. }
  82. return seqdesc.pEvent( 0 );
  83. }
  84. void BuildAllAnimationEventIndexes( CStudioHdr *pstudiohdr )
  85. {
  86. if ( !pstudiohdr )
  87. return;
  88. if( pstudiohdr->GetEventListVersion() != g_nEventListVersion )
  89. {
  90. for ( int i = 0 ; i < pstudiohdr->GetNumSeq() ; i++ )
  91. {
  92. SetEventIndexForSequence( pstudiohdr->pSeqdesc( i ) );
  93. }
  94. pstudiohdr->SetEventListVersion( g_nEventListVersion );
  95. }
  96. }
  97. //-----------------------------------------------------------------------------
  98. // Purpose: Ensures that activity / index relationship is recalculated
  99. // Input :
  100. // Output :
  101. //-----------------------------------------------------------------------------
  102. void ResetEventIndexes( CStudioHdr *pstudiohdr )
  103. {
  104. if (! pstudiohdr)
  105. return;
  106. pstudiohdr->SetEventListVersion( g_nEventListVersion - 1 );
  107. }
  108. //-----------------------------------------------------------------------------
  109. // Purpose:
  110. //-----------------------------------------------------------------------------
  111. void SetActivityForSequence( CStudioHdr *pstudiohdr, int i )
  112. {
  113. int iActivityIndex;
  114. const char *pszActivityName;
  115. mstudioseqdesc_t &seqdesc = pstudiohdr->pSeqdesc( i );
  116. seqdesc.flags |= STUDIO_ACTIVITY;
  117. pszActivityName = GetSequenceActivityName( pstudiohdr, i );
  118. if ( pszActivityName[0] != '\0' )
  119. {
  120. iActivityIndex = ActivityList_IndexForName( pszActivityName );
  121. if ( iActivityIndex == -1 )
  122. {
  123. // Allow this now. Animators can create custom activities that are referenced only on the client or by scripts, etc.
  124. //Warning( "***\nModel %s tried to reference unregistered activity: %s \n***\n", pstudiohdr->name, pszActivityName );
  125. //Assert(0);
  126. // HACK: the client and server don't share the private activity list so registering it on the client would hose the server
  127. #ifdef CLIENT_DLL
  128. seqdesc.flags &= ~STUDIO_ACTIVITY;
  129. #else
  130. seqdesc.activity = ActivityList_RegisterPrivateActivity( pszActivityName );
  131. #endif
  132. }
  133. else
  134. {
  135. seqdesc.activity = iActivityIndex;
  136. }
  137. }
  138. }
  139. //=========================================================
  140. // IndexModelSequences - set activity and event indexes for all model
  141. // sequences that have them.
  142. //=========================================================
  143. void IndexModelSequences( CStudioHdr *pstudiohdr )
  144. {
  145. int i;
  146. if (! pstudiohdr)
  147. return;
  148. if (!pstudiohdr->SequencesAvailable())
  149. return;
  150. for ( i = 0 ; i < pstudiohdr->GetNumSeq() ; i++ )
  151. {
  152. SetActivityForSequence( pstudiohdr, i );
  153. SetEventIndexForSequence( pstudiohdr->pSeqdesc( i ) );
  154. }
  155. pstudiohdr->SetActivityListVersion( g_nActivityListVersion );
  156. }
  157. //-----------------------------------------------------------------------------
  158. // Purpose: Ensures that activity / index relationship is recalculated
  159. // Input :
  160. // Output :
  161. //-----------------------------------------------------------------------------
  162. void ResetActivityIndexes( CStudioHdr *pstudiohdr )
  163. {
  164. if (! pstudiohdr)
  165. return;
  166. pstudiohdr->SetActivityListVersion( g_nActivityListVersion - 1 );
  167. }
  168. void VerifySequenceIndex( CStudioHdr *pstudiohdr )
  169. {
  170. if ( !pstudiohdr )
  171. {
  172. return;
  173. }
  174. if( pstudiohdr->GetActivityListVersion( ) != g_nActivityListVersion )
  175. {
  176. // this model's sequences have not yet been indexed by activity
  177. IndexModelSequences( pstudiohdr );
  178. }
  179. }
  180. #if !defined( MAKEXVCD )
  181. bool IsInPrediction()
  182. {
  183. return CBaseEntity::GetPredictionPlayer() != NULL;
  184. }
  185. int SelectWeightedSequence( CStudioHdr *pstudiohdr, int activity, int curSequence )
  186. {
  187. VPROF( "SelectWeightedSequence" );
  188. if (! pstudiohdr)
  189. return 0;
  190. if (!pstudiohdr->SequencesAvailable())
  191. return 0;
  192. VerifySequenceIndex( pstudiohdr );
  193. #if STUDIO_SEQUENCE_ACTIVITY_LOOKUPS_ARE_SLOW
  194. int weighttotal = 0;
  195. int seq = ACTIVITY_NOT_AVAILABLE;
  196. int weight = 0;
  197. for (int i = 0; i < pstudiohdr->GetNumSeq(); i++)
  198. {
  199. int curActivity = GetSequenceActivity( pstudiohdr, i, &weight );
  200. if (curActivity == activity)
  201. {
  202. if ( curSequence == i && weight < 0 )
  203. {
  204. seq = i;
  205. break;
  206. }
  207. weighttotal += iabs(weight);
  208. int randomValue;
  209. if ( IsInPrediction() )
  210. randomValue = SharedRandomInt( "SelectWeightedSequence", 0, weighttotal - 1, i );
  211. else
  212. randomValue = RandomInt( 0, weighttotal - 1 );
  213. if (!weighttotal || randomValue < iabs(weight))
  214. seq = i;
  215. }
  216. }
  217. return seq;
  218. #else
  219. return pstudiohdr->SelectWeightedSequence( activity, curSequence );
  220. #endif
  221. }
  222. // Pick a sequence for the given activity. If the current sequence is appropriate for the
  223. // current activity, and its stored weight is negative (whatever that means), always select
  224. // it. Otherwise perform a weighted selection -- imagine a large roulette wheel, with each
  225. // sequence having a number of spaces corresponding to its weight.
  226. int CStudioHdr::CActivityToSequenceMapping::SelectWeightedSequence( CStudioHdr *pstudiohdr, int activity, int curSequence )
  227. {
  228. if (!ValidateAgainst(pstudiohdr))
  229. {
  230. AssertMsg1(false, "CStudioHdr %s has changed its vmodel pointer without reinitializing its activity mapping! Now performing emergency reinitialization.", pstudiohdr->pszName());
  231. ExecuteOnce(DebuggerBreakIfDebugging());
  232. Reinitialize(pstudiohdr);
  233. }
  234. // a null m_pSequenceTuples just means that this studio header has no activities.
  235. if (!m_pSequenceTuples)
  236. return ACTIVITY_NOT_AVAILABLE;
  237. // is the current sequence appropriate?
  238. if (curSequence >= 0)
  239. {
  240. mstudioseqdesc_t &seqdesc = pstudiohdr->pSeqdesc( curSequence );
  241. if (seqdesc.activity == activity && seqdesc.actweight < 0)
  242. return curSequence;
  243. }
  244. // get the data for the given activity
  245. HashValueType dummy( activity, 0, 0, 0 );
  246. UtlHashHandle_t handle = m_ActToSeqHash.Find(dummy);
  247. if (!m_ActToSeqHash.IsValidHandle(handle))
  248. {
  249. return ACTIVITY_NOT_AVAILABLE;
  250. }
  251. const HashValueType * __restrict actData = &m_ActToSeqHash[handle];
  252. int weighttotal = actData->totalWeight;
  253. // generate a random number from 0 to the total weight
  254. int randomValue;
  255. if ( IsInPrediction() )
  256. {
  257. randomValue = SharedRandomInt( "SelectWeightedSequence", 0, weighttotal - 1 );
  258. }
  259. else
  260. {
  261. randomValue = RandomInt( 0, weighttotal - 1 );
  262. }
  263. // chug through the entries in the list (they are sequential therefore cache-coherent)
  264. // until we run out of random juice
  265. SequenceTuple * __restrict sequenceInfo = m_pSequenceTuples + actData->startingIdx;
  266. const SequenceTuple *const stopHere = sequenceInfo + actData->count; // this is a backup
  267. // in case the weights are somehow miscalculated -- we don't read or write through
  268. // it (because it aliases the restricted pointer above); it's only here for
  269. // the comparison.
  270. while (randomValue >= sequenceInfo->weight && sequenceInfo < stopHere)
  271. {
  272. randomValue -= sequenceInfo->weight;
  273. ++sequenceInfo;
  274. }
  275. return sequenceInfo->seqnum;
  276. }
  277. int CStudioHdr::CActivityToSequenceMapping::SelectWeightedSequenceFromModifiers( CStudioHdr *pstudiohdr, int activity, CUtlSymbol *pActivityModifiers, int iModifierCount )
  278. {
  279. if ( !pstudiohdr->SequencesAvailable() )
  280. {
  281. return ACTIVITY_NOT_AVAILABLE;
  282. }
  283. VerifySequenceIndex( pstudiohdr );
  284. if ( pstudiohdr->GetNumSeq() == 1 )
  285. {
  286. return ( ::GetSequenceActivity( pstudiohdr, 0, NULL ) == activity ) ? 0 : ACTIVITY_NOT_AVAILABLE;
  287. }
  288. if (!ValidateAgainst(pstudiohdr))
  289. {
  290. AssertMsg1(false, "CStudioHdr %s has changed its vmodel pointer without reinitializing its activity mapping! Now performing emergency reinitialization.", pstudiohdr->pszName());
  291. ExecuteOnce(DebuggerBreakIfDebugging());
  292. Reinitialize(pstudiohdr);
  293. }
  294. // a null m_pSequenceTuples just means that this studio header has no activities.
  295. if (!m_pSequenceTuples)
  296. return ACTIVITY_NOT_AVAILABLE;
  297. // get the data for the given activity
  298. HashValueType dummy( activity, 0, 0, 0 );
  299. UtlHashHandle_t handle = m_ActToSeqHash.Find(dummy);
  300. if (!m_ActToSeqHash.IsValidHandle(handle))
  301. {
  302. return ACTIVITY_NOT_AVAILABLE;
  303. }
  304. const HashValueType * __restrict actData = &m_ActToSeqHash[handle];
  305. // go through each sequence and give it a score
  306. int top_score = -1;
  307. CUtlVector<int> topScoring( actData->count, actData->count );
  308. for ( int i = 0; i < actData->count; i++ )
  309. {
  310. SequenceTuple * __restrict sequenceInfo = m_pSequenceTuples + actData->startingIdx + i;
  311. int score = 0;
  312. // count matching activity modifiers
  313. for ( int m = 0; m < iModifierCount; m++ )
  314. {
  315. int num_modifiers = sequenceInfo->iNumActivityModifiers;
  316. for ( int k = 0; k < num_modifiers; k++ )
  317. {
  318. if ( sequenceInfo->pActivityModifiers[ k ] == pActivityModifiers[ m ] )
  319. {
  320. score++;
  321. break;
  322. }
  323. }
  324. }
  325. if ( score > top_score )
  326. {
  327. topScoring.RemoveAll();
  328. topScoring.AddToTail( sequenceInfo->seqnum );
  329. top_score = score;
  330. }
  331. }
  332. // randomly pick between the highest scoring sequences ( NOTE: this method of selecting a sequence ignores activity weights )
  333. if ( IsInPrediction() )
  334. {
  335. return topScoring[ SharedRandomInt( "SelectWeightedSequence", 0, topScoring.Count() - 1 ) ];
  336. }
  337. return topScoring[ RandomInt( 0, topScoring.Count() - 1 ) ];
  338. }
  339. #endif
  340. int SelectHeaviestSequence( CStudioHdr *pstudiohdr, int activity )
  341. {
  342. if ( !pstudiohdr )
  343. return 0;
  344. VerifySequenceIndex( pstudiohdr );
  345. int maxweight = 0;
  346. int seq = ACTIVITY_NOT_AVAILABLE;
  347. int weight = 0;
  348. for (int i = 0; i < pstudiohdr->GetNumSeq(); i++)
  349. {
  350. int curActivity = GetSequenceActivity( pstudiohdr, i, &weight );
  351. if (curActivity == activity)
  352. {
  353. if ( iabs(weight) > maxweight )
  354. {
  355. maxweight = iabs(weight);
  356. seq = i;
  357. }
  358. }
  359. }
  360. return seq;
  361. }
  362. void GetEyePosition ( CStudioHdr *pstudiohdr, Vector &vecEyePosition )
  363. {
  364. if ( !pstudiohdr )
  365. {
  366. Warning( "GetEyePosition() Can't get pstudiohdr ptr!\n" );
  367. return;
  368. }
  369. vecEyePosition = pstudiohdr->eyeposition();
  370. }
  371. //-----------------------------------------------------------------------------
  372. // Purpose: Looks up an activity by name.
  373. // Input : label - Name of the activity to look up, ie "ACT_IDLE"
  374. // Output : Activity index or ACT_INVALID if not found.
  375. //-----------------------------------------------------------------------------
  376. int LookupActivity( CStudioHdr *pstudiohdr, const char *label )
  377. {
  378. VPROF( "LookupActivity" );
  379. if ( !pstudiohdr )
  380. {
  381. return 0;
  382. }
  383. for ( int i = 0; i < pstudiohdr->GetNumSeq(); i++ )
  384. {
  385. mstudioseqdesc_t &seqdesc = pstudiohdr->pSeqdesc( i );
  386. if ( stricmp( seqdesc.pszActivityName(), label ) == 0 )
  387. {
  388. return seqdesc.activity;
  389. }
  390. }
  391. return ACT_INVALID;
  392. }
  393. #if !defined( MAKEXVCD )
  394. //-----------------------------------------------------------------------------
  395. // Purpose: Looks up a sequence by sequence name first, then by activity name.
  396. // Input : label - The sequence name or activity name to look up.
  397. // Output : Returns the sequence index of the matching sequence, or ACT_INVALID.
  398. //-----------------------------------------------------------------------------
  399. int LookupSequence( CStudioHdr *pstudiohdr, const char *label )
  400. {
  401. VPROF( "LookupSequence" );
  402. if (! pstudiohdr)
  403. return 0;
  404. if (!pstudiohdr->SequencesAvailable())
  405. return 0;
  406. //
  407. // Look up by sequence name.
  408. //
  409. for (int i = 0; i < pstudiohdr->GetNumSeq(); i++)
  410. {
  411. mstudioseqdesc_t &seqdesc = pstudiohdr->pSeqdesc( i );
  412. if (stricmp( seqdesc.pszLabel(), label ) == 0)
  413. return i;
  414. }
  415. //
  416. // Not found, look up by activity name.
  417. //
  418. int nActivity = LookupActivity( pstudiohdr, label );
  419. if (nActivity != ACT_INVALID )
  420. {
  421. return SelectWeightedSequence( pstudiohdr, nActivity );
  422. }
  423. return ACT_INVALID;
  424. }
  425. void GetSequenceLinearMotion( CStudioHdr *pstudiohdr, int iSequence, const float poseParameter[], Vector *pVec )
  426. {
  427. if ( !pstudiohdr)
  428. {
  429. ExecuteNTimes( 20, Msg( "Bad pstudiohdr in GetSequenceLinearMotion()!\n" ) );
  430. return;
  431. }
  432. if (!pstudiohdr->SequencesAvailable())
  433. return;
  434. if( iSequence < 0 || iSequence >= pstudiohdr->GetNumSeq() )
  435. {
  436. // Don't spam on bogus model
  437. if ( pstudiohdr->GetNumSeq() > 0 )
  438. {
  439. ExecuteNTimes( 20, Msg( "Bad sequence (%i out of %i max) in GetSequenceLinearMotion() for model '%s'!\n", iSequence, pstudiohdr->GetNumSeq(), pstudiohdr->pszName() ) );
  440. }
  441. pVec->Init();
  442. return;
  443. }
  444. QAngle vecAngles;
  445. Studio_SeqMovement( pstudiohdr, iSequence, 0, 1.0, poseParameter, (*pVec), vecAngles );
  446. }
  447. #endif
  448. const char *GetSequenceName( CStudioHdr *pstudiohdr, int iSequence )
  449. {
  450. if( !pstudiohdr || iSequence < 0 || iSequence >= pstudiohdr->GetNumSeq() )
  451. {
  452. if ( pstudiohdr )
  453. {
  454. Msg( "Bad sequence in GetSequenceName() for model '%s'!\n", pstudiohdr->pszName() );
  455. }
  456. return "Unknown";
  457. }
  458. mstudioseqdesc_t &seqdesc = pstudiohdr->pSeqdesc( iSequence );
  459. return seqdesc.pszLabel();
  460. }
  461. const char *GetSequenceActivityName( CStudioHdr *pstudiohdr, int iSequence )
  462. {
  463. if( !pstudiohdr || iSequence < 0 || iSequence >= pstudiohdr->GetNumSeq() )
  464. {
  465. if ( pstudiohdr )
  466. {
  467. Msg( "Bad sequence in GetSequenceActivityName() for model '%s'!\n", pstudiohdr->pszName() );
  468. }
  469. return "Unknown";
  470. }
  471. mstudioseqdesc_t &seqdesc = pstudiohdr->pSeqdesc( iSequence );
  472. return seqdesc.pszActivityName( );
  473. }
  474. int GetSequenceFlags( CStudioHdr *pstudiohdr, int sequence )
  475. {
  476. if ( !pstudiohdr ||
  477. !pstudiohdr->SequencesAvailable() ||
  478. sequence < 0 ||
  479. sequence >= pstudiohdr->GetNumSeq() )
  480. {
  481. return 0;
  482. }
  483. mstudioseqdesc_t &seqdesc = pstudiohdr->pSeqdesc( sequence );
  484. return seqdesc.flags;
  485. }
  486. //-----------------------------------------------------------------------------
  487. // Purpose:
  488. // Input : *pstudiohdr -
  489. // sequence -
  490. // type -
  491. // Output : Returns true on success, false on failure.
  492. //-----------------------------------------------------------------------------
  493. bool HasAnimationEventOfType( CStudioHdr *pstudiohdr, int sequence, int type )
  494. {
  495. if ( !pstudiohdr || sequence >= pstudiohdr->GetNumSeq() )
  496. return false;
  497. mstudioseqdesc_t &seqdesc = pstudiohdr->pSeqdesc( sequence );
  498. if ( !&seqdesc )
  499. return false;
  500. mstudioevent_t *pevent = GetEventIndexForSequence( seqdesc );
  501. if ( !pevent )
  502. return false;
  503. if (seqdesc.numevents == 0 )
  504. return false;
  505. int index;
  506. for ( index = 0; index < (int)seqdesc.numevents; index++ )
  507. {
  508. if ( pevent[ index ].event == type )
  509. {
  510. return true;
  511. }
  512. }
  513. return false;
  514. }
  515. int GetAnimationEvent( CStudioHdr *pstudiohdr, int sequence, animevent_t *pNPCEvent, float flStart, float flEnd, int index )
  516. {
  517. if ( !pstudiohdr || sequence >= pstudiohdr->GetNumSeq() || !pNPCEvent )
  518. return 0;
  519. mstudioseqdesc_t &seqdesc = pstudiohdr->pSeqdesc( sequence );
  520. if (seqdesc.numevents == 0 || index >= (int)seqdesc.numevents )
  521. return 0;
  522. // Msg( "flStart %f flEnd %f (%d) %s\n", flStart, flEnd, seqdesc.numevents, seqdesc.label );
  523. mstudioevent_t *pevent = GetEventIndexForSequence( seqdesc );
  524. for (; index < (int)seqdesc.numevents; index++)
  525. {
  526. // Don't send client-side events to the server AI
  527. if ( pevent[index].type & AE_TYPE_NEWEVENTSYSTEM )
  528. {
  529. if ( !(pevent[index].type & AE_TYPE_SERVER) )
  530. continue;
  531. }
  532. else if ( pevent[index].event >= EVENT_CLIENT ) //Adrian - Support the old event system
  533. continue;
  534. bool bOverlapEvent = false;
  535. if (pevent[index].cycle >= flStart && pevent[index].cycle < flEnd)
  536. {
  537. bOverlapEvent = true;
  538. }
  539. // FIXME: doesn't work with animations being played in reverse
  540. else if ((seqdesc.flags & STUDIO_LOOPING) && flEnd < flStart)
  541. {
  542. if (pevent[index].cycle >= flStart || pevent[index].cycle < flEnd)
  543. {
  544. bOverlapEvent = true;
  545. }
  546. }
  547. if (bOverlapEvent)
  548. {
  549. pNPCEvent->pSource = NULL;
  550. pNPCEvent->cycle = pevent[index].cycle;
  551. #if !defined( MAKEXVCD )
  552. pNPCEvent->eventtime = gpGlobals->curtime;
  553. #else
  554. pNPCEvent->eventtime = 0.0f;
  555. #endif
  556. pNPCEvent->event = pevent[index].event;
  557. pNPCEvent->options = pevent[index].pszOptions();
  558. pNPCEvent->type = pevent[index].type;
  559. return index + 1;
  560. }
  561. }
  562. return 0;
  563. }
  564. int FindTransitionSequence( CStudioHdr *pstudiohdr, int iCurrentSequence, int iGoalSequence, int *piDir )
  565. {
  566. if ( !pstudiohdr )
  567. return iGoalSequence;
  568. if ( !pstudiohdr->SequencesAvailable() )
  569. return iGoalSequence;
  570. if ( ( iCurrentSequence < 0 ) || ( iCurrentSequence >= pstudiohdr->GetNumSeq() ) )
  571. return iGoalSequence;
  572. if ( ( iGoalSequence < 0 ) || ( iGoalSequence >= pstudiohdr->GetNumSeq() ) )
  573. {
  574. // asking for a bogus sequence. Punt.
  575. Assert( 0 );
  576. return iGoalSequence;
  577. }
  578. // bail if we're going to or from a node 0
  579. if (pstudiohdr->EntryNode( iCurrentSequence ) == 0 || pstudiohdr->EntryNode( iGoalSequence ) == 0)
  580. {
  581. *piDir = 1;
  582. return iGoalSequence;
  583. }
  584. int iEndNode;
  585. // Msg( "from %d to %d: ", pEndNode->iEndNode, pGoalNode->iStartNode );
  586. // check to see if we should be going forward or backward through the graph
  587. if (*piDir > 0)
  588. {
  589. iEndNode = pstudiohdr->ExitNode( iCurrentSequence );
  590. }
  591. else
  592. {
  593. iEndNode = pstudiohdr->EntryNode( iCurrentSequence );
  594. }
  595. // if both sequences are on the same node, just go there
  596. if (iEndNode == pstudiohdr->EntryNode( iGoalSequence ))
  597. {
  598. *piDir = 1;
  599. return iGoalSequence;
  600. }
  601. int iInternNode = pstudiohdr->GetTransition( iEndNode, pstudiohdr->EntryNode( iGoalSequence ) );
  602. // if there is no transitionial node, just go to the goal sequence
  603. if (iInternNode == 0)
  604. return iGoalSequence;
  605. int i;
  606. // look for someone going from the entry node to next node it should hit
  607. // this may be the goal sequences node or an intermediate node
  608. for (i = 0; i < pstudiohdr->GetNumSeq(); i++)
  609. {
  610. mstudioseqdesc_t &seqdesc = pstudiohdr->pSeqdesc(i );
  611. if (pstudiohdr->EntryNode( i ) == iEndNode && pstudiohdr->ExitNode( i ) == iInternNode)
  612. {
  613. *piDir = 1;
  614. return i;
  615. }
  616. if (seqdesc.nodeflags)
  617. {
  618. if (pstudiohdr->ExitNode( i ) == iEndNode && pstudiohdr->EntryNode( i ) == iInternNode)
  619. {
  620. *piDir = -1;
  621. return i;
  622. }
  623. }
  624. }
  625. // this means that two parts of the node graph are not connected.
  626. DevMsg( 2, "error in transition graph: %s to %s\n", pstudiohdr->pszNodeName( iEndNode ), pstudiohdr->pszNodeName( pstudiohdr->EntryNode( iGoalSequence ) ));
  627. // Go ahead and jump to the goal sequence
  628. return iGoalSequence;
  629. }
  630. bool GotoSequence( CStudioHdr *pstudiohdr, int iCurrentSequence, float flCurrentCycle, float flCurrentRate, int iGoalSequence, int &nNextSequence, float &flNextCycle, int &iNextDir )
  631. {
  632. if ( !pstudiohdr )
  633. return false;
  634. if ( !pstudiohdr->SequencesAvailable() )
  635. return false;
  636. if ( ( iCurrentSequence < 0 ) || ( iCurrentSequence >= pstudiohdr->GetNumSeq() ) )
  637. return false;
  638. if ( ( iGoalSequence < 0 ) || ( iGoalSequence >= pstudiohdr->GetNumSeq() ) )
  639. {
  640. // asking for a bogus sequence. Punt.
  641. Assert( 0 );
  642. return false;
  643. }
  644. // bail if we're going to or from a node 0
  645. if (pstudiohdr->EntryNode( iCurrentSequence ) == 0 || pstudiohdr->EntryNode( iGoalSequence ) == 0)
  646. {
  647. iNextDir = 1;
  648. flNextCycle = 0.0;
  649. nNextSequence = iGoalSequence;
  650. return true;
  651. }
  652. int iEndNode = pstudiohdr->ExitNode( iCurrentSequence );
  653. // Msg( "from %d to %d: ", pEndNode->iEndNode, pGoalNode->iStartNode );
  654. // if we're in a transition sequence
  655. if (pstudiohdr->EntryNode( iCurrentSequence ) != pstudiohdr->ExitNode( iCurrentSequence ))
  656. {
  657. // are we done with it?
  658. if (flCurrentRate > 0.0 && flCurrentCycle >= 0.999)
  659. {
  660. iEndNode = pstudiohdr->ExitNode( iCurrentSequence );
  661. }
  662. else if (flCurrentRate < 0.0 && flCurrentCycle <= 0.001)
  663. {
  664. iEndNode = pstudiohdr->EntryNode( iCurrentSequence );
  665. }
  666. else
  667. {
  668. // nope, exit
  669. return false;
  670. }
  671. }
  672. // if both sequences are on the same node, just go there
  673. if (iEndNode == pstudiohdr->EntryNode( iGoalSequence ))
  674. {
  675. iNextDir = 1;
  676. flNextCycle = 0.0;
  677. nNextSequence = iGoalSequence;
  678. return true;
  679. }
  680. int iInternNode = pstudiohdr->GetTransition( iEndNode, pstudiohdr->EntryNode( iGoalSequence ) );
  681. // if there is no transitionial node, just go to the goal sequence
  682. if (iInternNode == 0)
  683. {
  684. iNextDir = 1;
  685. flNextCycle = 0.0;
  686. nNextSequence = iGoalSequence;
  687. return true;
  688. }
  689. int i;
  690. // look for someone going from the entry node to next node it should hit
  691. // this may be the goal sequences node or an intermediate node
  692. for (i = 0; i < pstudiohdr->GetNumSeq(); i++)
  693. {
  694. mstudioseqdesc_t &seqdesc = pstudiohdr->pSeqdesc(i );
  695. if (pstudiohdr->EntryNode( i ) == iEndNode && pstudiohdr->ExitNode( i ) == iInternNode)
  696. {
  697. iNextDir = 1;
  698. flNextCycle = 0.0;
  699. nNextSequence = i;
  700. return true;
  701. }
  702. if (seqdesc.nodeflags)
  703. {
  704. if (pstudiohdr->ExitNode( i ) == iEndNode && pstudiohdr->EntryNode( i ) == iInternNode)
  705. {
  706. iNextDir = -1;
  707. flNextCycle = 0.999;
  708. nNextSequence = i;
  709. return true;
  710. }
  711. }
  712. }
  713. // this means that two parts of the node graph are not connected.
  714. DevMsg( 2, "error in transition graph: %s to %s\n", pstudiohdr->pszNodeName( iEndNode ), pstudiohdr->pszNodeName( pstudiohdr->EntryNode( iGoalSequence ) ));
  715. return false;
  716. }
  717. void SetBodygroup( CStudioHdr *pstudiohdr, int& body, int iGroup, int iValue )
  718. {
  719. if (! pstudiohdr)
  720. return;
  721. if (iGroup >= pstudiohdr->numbodyparts())
  722. return;
  723. mstudiobodyparts_t *pbodypart = pstudiohdr->pBodypart( iGroup );
  724. if (iValue >= pbodypart->nummodels)
  725. return;
  726. int iCurrent = (body / pbodypart->base) % pbodypart->nummodels;
  727. body = (body - (iCurrent * pbodypart->base) + (iValue * pbodypart->base));
  728. }
  729. int GetBodygroup( CStudioHdr *pstudiohdr, int body, int iGroup )
  730. {
  731. if (! pstudiohdr)
  732. return 0;
  733. if (iGroup >= pstudiohdr->numbodyparts())
  734. return 0;
  735. mstudiobodyparts_t *pbodypart = pstudiohdr->pBodypart( iGroup );
  736. if (pbodypart->nummodels <= 1)
  737. return 0;
  738. int iCurrent = (body / pbodypart->base) % pbodypart->nummodels;
  739. return iCurrent;
  740. }
  741. const char *GetBodygroupName( CStudioHdr *pstudiohdr, int iGroup )
  742. {
  743. if ( !pstudiohdr)
  744. return "";
  745. if (iGroup >= pstudiohdr->numbodyparts())
  746. return "";
  747. mstudiobodyparts_t *pbodypart = pstudiohdr->pBodypart( iGroup );
  748. return pbodypart->pszName();
  749. }
  750. int FindBodygroupByName( CStudioHdr *pstudiohdr, const char *name )
  751. {
  752. if ( !pstudiohdr || !pstudiohdr->IsValid() )
  753. return -1;
  754. int group;
  755. for ( group = 0; group < pstudiohdr->numbodyparts(); group++ )
  756. {
  757. mstudiobodyparts_t *pbodypart = pstudiohdr->pBodypart( group );
  758. if ( !Q_strcasecmp( name, pbodypart->pszName() ) )
  759. {
  760. return group;
  761. }
  762. }
  763. return -1;
  764. }
  765. int GetBodygroupCount( CStudioHdr *pstudiohdr, int iGroup )
  766. {
  767. if ( !pstudiohdr )
  768. return 0;
  769. if (iGroup >= pstudiohdr->numbodyparts())
  770. return 0;
  771. mstudiobodyparts_t *pbodypart = pstudiohdr->pBodypart( iGroup );
  772. return pbodypart->nummodels;
  773. }
  774. int GetNumBodyGroups( CStudioHdr *pstudiohdr )
  775. {
  776. if ( !pstudiohdr )
  777. return 0;
  778. return pstudiohdr->numbodyparts();
  779. }
  780. int GetSequenceActivity( CStudioHdr *pstudiohdr, int sequence, int *pweight )
  781. {
  782. if (!pstudiohdr || !pstudiohdr->SequencesAvailable() )
  783. {
  784. if (pweight)
  785. *pweight = 0;
  786. return 0;
  787. }
  788. mstudioseqdesc_t &seqdesc = pstudiohdr->pSeqdesc( sequence );
  789. if (!(seqdesc.flags & STUDIO_ACTIVITY))
  790. {
  791. SetActivityForSequence( pstudiohdr, sequence );
  792. }
  793. if (pweight)
  794. *pweight = seqdesc.actweight;
  795. return seqdesc.activity;
  796. }
  797. void GetAttachmentLocalSpace( CStudioHdr *pstudiohdr, int attachIndex, matrix3x4_t &pLocalToWorld )
  798. {
  799. if ( attachIndex >= 0 )
  800. {
  801. const mstudioattachment_t &pAttachment = pstudiohdr->pAttachment(attachIndex);
  802. MatrixCopy( pAttachment.local, pLocalToWorld );
  803. }
  804. }
  805. //-----------------------------------------------------------------------------
  806. // Purpose:
  807. // Input : *pstudiohdr -
  808. // *name -
  809. // Output : int
  810. //-----------------------------------------------------------------------------
  811. int FindHitboxSetByName( CStudioHdr *pstudiohdr, const char *name )
  812. {
  813. if ( !pstudiohdr )
  814. return -1;
  815. for ( int i = 0; i < pstudiohdr->numhitboxsets(); i++ )
  816. {
  817. mstudiohitboxset_t *set = pstudiohdr->pHitboxSet( i );
  818. if ( !set )
  819. continue;
  820. if ( !stricmp( set->pszName(), name ) )
  821. return i;
  822. }
  823. return -1;
  824. }
  825. //-----------------------------------------------------------------------------
  826. // Purpose:
  827. // Input : *pstudiohdr -
  828. // setnumber -
  829. // Output : char const
  830. //-----------------------------------------------------------------------------
  831. const char *GetHitboxSetName( CStudioHdr *pstudiohdr, int setnumber )
  832. {
  833. if ( !pstudiohdr )
  834. return "";
  835. mstudiohitboxset_t *set = pstudiohdr->pHitboxSet( setnumber );
  836. if ( !set )
  837. return "";
  838. return set->pszName();
  839. }
  840. //-----------------------------------------------------------------------------
  841. // Purpose:
  842. // Input : *pstudiohdr -
  843. // Output : int
  844. //-----------------------------------------------------------------------------
  845. int GetHitboxSetCount( CStudioHdr *pstudiohdr )
  846. {
  847. if ( !pstudiohdr )
  848. return 0;
  849. return pstudiohdr->numhitboxsets();
  850. }