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.

1356 lines
35 KiB

  1. //========= Copyright � 1996-2005, 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. if ( seqdesc.numevents == 0 )
  53. return;
  54. #ifndef CLIENT_DLL
  55. seqdesc.flags |= STUDIO_EVENT;
  56. #else
  57. seqdesc.flags |= STUDIO_EVENT_CLIENT;
  58. #endif
  59. for ( int index = 0; index < (int)seqdesc.numevents; index++ )
  60. {
  61. mstudioevent_t *pevent = (mstudioevent_for_client_server_t*)seqdesc.pEvent( index );
  62. if ( !pevent )
  63. continue;
  64. if ( pevent->type & AE_TYPE_NEWEVENTSYSTEM )
  65. {
  66. const char *pEventName = pevent->pszEventName();
  67. int iEventIndex = EventList_IndexForName( pEventName );
  68. if ( iEventIndex == -1 )
  69. {
  70. pevent->event_newsystem = EventList_RegisterPrivateEvent( pEventName );
  71. }
  72. else
  73. {
  74. pevent->event_newsystem = iEventIndex;
  75. pevent->type |= EventList_GetEventType( iEventIndex );
  76. }
  77. }
  78. }
  79. }
  80. mstudioevent_for_client_server_t *GetEventIndexForSequence( mstudioseqdesc_t &seqdesc )
  81. {
  82. #ifndef CLIENT_DLL
  83. if ( !(seqdesc.flags & STUDIO_EVENT) )
  84. #else
  85. if ( !(seqdesc.flags & STUDIO_EVENT_CLIENT) )
  86. #endif
  87. {
  88. SetEventIndexForSequence( seqdesc );
  89. }
  90. return (mstudioevent_for_client_server_t*)seqdesc.pEvent( 0 );
  91. }
  92. void BuildAllAnimationEventIndexes( CStudioHdr *pstudiohdr )
  93. {
  94. if ( !pstudiohdr )
  95. return;
  96. if( pstudiohdr->GetEventListVersion() != g_nEventListVersion )
  97. {
  98. for ( int i = 0 ; i < pstudiohdr->GetNumSeq() ; i++ )
  99. {
  100. SetEventIndexForSequence( pstudiohdr->pSeqdesc( i ) );
  101. }
  102. pstudiohdr->SetEventListVersion( g_nEventListVersion );
  103. }
  104. }
  105. //-----------------------------------------------------------------------------
  106. // Purpose: Ensures that activity / index relationship is recalculated
  107. // Input :
  108. // Output :
  109. //-----------------------------------------------------------------------------
  110. void ResetEventIndexes( CStudioHdr *pstudiohdr )
  111. {
  112. if (! pstudiohdr)
  113. return;
  114. pstudiohdr->SetEventListVersion( g_nEventListVersion - 1 );
  115. }
  116. //-----------------------------------------------------------------------------
  117. // Purpose:
  118. //-----------------------------------------------------------------------------
  119. void SetActivityForSequence( CStudioHdr *pstudiohdr, int i )
  120. {
  121. int iActivityIndex;
  122. const char *pszActivityName;
  123. mstudioseqdesc_t &seqdesc = pstudiohdr->pSeqdesc( i );
  124. seqdesc.flags |= STUDIO_ACTIVITY;
  125. pszActivityName = GetSequenceActivityName( pstudiohdr, i );
  126. if ( pszActivityName[0] != '\0' )
  127. {
  128. iActivityIndex = ActivityList_IndexForName( pszActivityName );
  129. if ( iActivityIndex == -1 )
  130. {
  131. // Allow this now. Animators can create custom activities that are referenced only on the client or by scripts, etc.
  132. //Warning( "***\nModel %s tried to reference unregistered activity: %s \n***\n", pstudiohdr->name, pszActivityName );
  133. //Assert(0);
  134. // HACK: the client and server don't share the private activity list so registering it on the client would hose the server
  135. #ifdef CLIENT_DLL
  136. seqdesc.flags &= ~STUDIO_ACTIVITY;
  137. #else
  138. seqdesc.activity = ActivityList_RegisterPrivateActivity( pszActivityName );
  139. #endif
  140. }
  141. else
  142. {
  143. seqdesc.activity = iActivityIndex;
  144. }
  145. }
  146. }
  147. //=========================================================
  148. // IndexModelSequences - set activity and event indexes for all model
  149. // sequences that have them.
  150. //=========================================================
  151. void IndexModelSequences( CStudioHdr *pstudiohdr )
  152. {
  153. VPROF_2( "IndexModelSequences", IsServerDll() ? VPROF_BUDGETGROUP_SERVER_ANIM : VPROF_BUDGETGROUP_CLIENT_ANIMATION, false, BUDGETFLAG_ALL );
  154. int i;
  155. if (! pstudiohdr)
  156. return;
  157. if (!pstudiohdr->SequencesAvailable())
  158. return;
  159. for ( i = 0 ; i < pstudiohdr->GetNumSeq() ; i++ )
  160. {
  161. SetActivityForSequence( pstudiohdr, i );
  162. SetEventIndexForSequence( pstudiohdr->pSeqdesc( i ) );
  163. }
  164. pstudiohdr->SetActivityListVersion( g_nActivityListVersion );
  165. }
  166. //-----------------------------------------------------------------------------
  167. // Purpose: Ensures that activity / index relationship is recalculated
  168. // Input :
  169. // Output :
  170. //-----------------------------------------------------------------------------
  171. void ResetActivityIndexes( CStudioHdr *pstudiohdr )
  172. {
  173. if (! pstudiohdr)
  174. return;
  175. pstudiohdr->SetActivityListVersion( g_nActivityListVersion - 1 );
  176. }
  177. #ifdef _DEBUG
  178. // used to test a reent change to verifysequenceindex below: can the client count on the server's
  179. // activity numbers?
  180. bool DebugValidateActivityIndexes( CStudioHdr *pstudiohdr )
  181. {
  182. bool bGood = true;
  183. int i;
  184. if (! pstudiohdr)
  185. return bGood;
  186. if (!pstudiohdr->SequencesAvailable())
  187. return bGood;
  188. for ( i = 0 ; i < pstudiohdr->GetNumSeq() ; i++ )
  189. {
  190. mstudioseqdesc_t &seqdesc = pstudiohdr->pSeqdesc( i );
  191. // test SetActivityForSequence( pstudiohdr, i );
  192. {
  193. const char *pszActivityName;
  194. pszActivityName = GetSequenceActivityName( pstudiohdr, i );
  195. if ( pszActivityName[0] != '\0' )
  196. {
  197. int iActIdx = ActivityList_IndexForName( pszActivityName );
  198. AssertMsg1( iActIdx == seqdesc.activity, "Client and server sequence activity index numbers differ for %s", pstudiohdr->pszName() );
  199. bGood = bGood && iActIdx == seqdesc.activity;
  200. }
  201. }
  202. // test SetEventIndexForSequence( pstudiohdr->pSeqdesc( i ) );
  203. {
  204. if ( &seqdesc == NULL )
  205. return bGood;
  206. if ( seqdesc.numevents == 0 )
  207. return bGood;
  208. for ( int index = 0; index < (int)seqdesc.numevents; index++ )
  209. {
  210. mstudioevent_t *pevent = (mstudioevent_for_client_server_t*)seqdesc.pEvent( index );
  211. if ( !pevent )
  212. continue;
  213. if ( pevent->type & AE_TYPE_NEWEVENTSYSTEM )
  214. {
  215. const char *pEventName = pevent->pszEventName();
  216. int iEventIndex = EventList_IndexForName( pEventName );
  217. AssertMsg1( iEventIndex == pevent->event_newsystem, "Client and server sequence event index numbers differ for %s", pstudiohdr->pszName() );
  218. bGood = bGood && iEventIndex == pevent->event_newsystem;
  219. }
  220. }
  221. }
  222. }
  223. return bGood;
  224. }
  225. #endif
  226. void VerifySequenceIndex( CStudioHdr *pstudiohdr )
  227. {
  228. if ( !pstudiohdr )
  229. {
  230. return;
  231. }
  232. if( pstudiohdr->GetActivityListVersion( ) < g_nActivityListVersion ) // sometimes the server's numbers can get ahead of the client's if we're sharing memory between the two, so it's only necessary to reindex if a model is lagging the version number.
  233. {
  234. // this model's sequences have not yet been indexed by activity
  235. IndexModelSequences( pstudiohdr );
  236. }
  237. #if 0
  238. else if ( pstudiohdr->GetActivityListVersion( ) != g_nActivityListVersion )
  239. {
  240. if ( !DebugValidateActivityIndexes( pstudiohdr ) )
  241. {
  242. Warning( "Client and server activity index numbers differ for %s\n", pstudiohdr->pszName() );
  243. }
  244. }
  245. #endif
  246. }
  247. #if !defined( MAKEXVCD )
  248. bool IsInPrediction()
  249. {
  250. return CBaseEntity::GetPredictionPlayer() != NULL;
  251. }
  252. int SelectWeightedSequence( CStudioHdr *pstudiohdr, int activity, int curSequence )
  253. {
  254. VPROF( "SelectWeightedSequence" );
  255. #ifdef CLIENT_DLL
  256. VPROF_INCREMENT_COUNTER( "Client SelectWeightedSequence", 1 );
  257. #else // ifdef GAME_DLL
  258. VPROF_INCREMENT_COUNTER( "Server SelectWeightedSequence", 1 );
  259. #endif
  260. if (! pstudiohdr)
  261. return 0;
  262. if (!pstudiohdr->SequencesAvailable())
  263. return 0;
  264. VerifySequenceIndex( pstudiohdr );
  265. int numSeq = pstudiohdr->GetNumSeq();
  266. if ( numSeq == 1 )
  267. {
  268. return ( GetSequenceActivity( pstudiohdr, 0, NULL ) == activity ) ? 0 : ACTIVITY_NOT_AVAILABLE;
  269. }
  270. return pstudiohdr->SelectWeightedSequence( activity, curSequence );
  271. }
  272. // Pick a sequence for the given activity. If the current sequence is appropriate for the
  273. // current activity, and its stored weight is negative (whatever that means), always select
  274. // it. Otherwise perform a weighted selection -- imagine a large roulette wheel, with each
  275. // sequence having a number of spaces corresponding to its weight.
  276. int CStudioHdr::CActivityToSequenceMapping::SelectWeightedSequence( CStudioHdr *pstudiohdr, int activity, int curSequence )
  277. {
  278. // is the current sequence appropriate?
  279. if (curSequence >= 0)
  280. {
  281. mstudioseqdesc_t &seqdesc = pstudiohdr->pSeqdesc( curSequence );
  282. if (seqdesc.activity == activity && seqdesc.actweight < 0)
  283. return curSequence;
  284. }
  285. if ( !pstudiohdr->SequencesAvailable() )
  286. {
  287. return ACTIVITY_NOT_AVAILABLE;
  288. }
  289. if ( pstudiohdr->GetNumSeq() == 1 )
  290. {
  291. AssertMsg( 0, "Expected single sequence case to be handled in ::SelectWeightedSequence()" );
  292. return ACTIVITY_NOT_AVAILABLE;
  293. }
  294. if (!ValidateAgainst(pstudiohdr))
  295. {
  296. AssertMsg1(false, "CStudioHdr %s has changed its vmodel pointer without reinitializing its activity mapping! Now performing emergency reinitialization.", pstudiohdr->pszName());
  297. ExecuteOnce(DebuggerBreakIfDebugging());
  298. Reinitialize(pstudiohdr);
  299. }
  300. // a null m_pSequenceTuples just means that this studio header has no activities.
  301. if (!m_pSequenceTuples)
  302. return ACTIVITY_NOT_AVAILABLE;
  303. // get the data for the given activity
  304. HashValueType dummy( activity, 0, 0, 0 );
  305. UtlHashHandle_t handle = m_ActToSeqHash.Find(dummy);
  306. if (!m_ActToSeqHash.IsValidHandle(handle))
  307. {
  308. return ACTIVITY_NOT_AVAILABLE;
  309. }
  310. const HashValueType * __restrict actData = &m_ActToSeqHash[handle];
  311. AssertMsg2( actData->totalWeight > 0, "Activity %d has total weight of %d!",
  312. activity, actData->totalWeight );
  313. int weighttotal = actData->totalWeight;
  314. // failsafe if the weight is 0: assume the artist screwed up and that the first sequence
  315. // for this activity should be returned.
  316. int randomValue = 0;
  317. if ( actData->totalWeight <= 0 )
  318. {
  319. Warning( "Activity %s has %d sequences with a total weight of %d!", ActivityList_NameForIndex(activity), actData->count, actData->totalWeight );
  320. return (m_pSequenceTuples + actData->startingIdx)->seqnum;
  321. }
  322. else if ( actData->totalWeight == 1 )
  323. {
  324. randomValue = 0;
  325. }
  326. else
  327. {
  328. // generate a random number from 0 to the total weight
  329. if ( IsInPrediction() )
  330. {
  331. randomValue = SharedRandomInt( "SelectWeightedSequence", 0, weighttotal - 1 );
  332. }
  333. else
  334. {
  335. randomValue = RandomInt( 0, weighttotal - 1 );
  336. }
  337. }
  338. // chug through the entries in the list (they are sequential therefore cache-coherent)
  339. // until we run out of random juice
  340. SequenceTuple * __restrict sequenceInfo = m_pSequenceTuples + actData->startingIdx;
  341. const SequenceTuple *const stopHere = sequenceInfo + actData->count; // this is a backup
  342. // in case the weights are somehow miscalculated -- we don't read or write through
  343. // it (because it aliases the restricted pointer above); it's only here for
  344. // the comparison.
  345. while (randomValue >= sequenceInfo->weight && sequenceInfo < stopHere)
  346. {
  347. randomValue -= sequenceInfo->weight;
  348. ++sequenceInfo;
  349. }
  350. return sequenceInfo->seqnum;
  351. }
  352. int CStudioHdr::CActivityToSequenceMapping::SelectWeightedSequenceFromModifiers( CStudioHdr *pstudiohdr, int activity, CUtlSymbol *pActivityModifiers, int iModifierCount )
  353. {
  354. if ( !pstudiohdr->SequencesAvailable() )
  355. {
  356. return ACTIVITY_NOT_AVAILABLE;
  357. }
  358. VerifySequenceIndex( pstudiohdr );
  359. if ( pstudiohdr->GetNumSeq() == 1 )
  360. {
  361. return ( ::GetSequenceActivity( pstudiohdr, 0, NULL ) == activity ) ? 0 : ACTIVITY_NOT_AVAILABLE;
  362. }
  363. if (!ValidateAgainst(pstudiohdr))
  364. {
  365. AssertMsg1(false, "CStudioHdr %s has changed its vmodel pointer without reinitializing its activity mapping! Now performing emergency reinitialization.", pstudiohdr->pszName());
  366. ExecuteOnce(DebuggerBreakIfDebugging());
  367. Reinitialize(pstudiohdr);
  368. }
  369. // a null m_pSequenceTuples just means that this studio header has no activities.
  370. if (!m_pSequenceTuples)
  371. return ACTIVITY_NOT_AVAILABLE;
  372. // get the data for the given activity
  373. HashValueType dummy( activity, 0, 0, 0 );
  374. UtlHashHandle_t handle = m_ActToSeqHash.Find(dummy);
  375. if (!m_ActToSeqHash.IsValidHandle(handle))
  376. {
  377. return ACTIVITY_NOT_AVAILABLE;
  378. }
  379. const HashValueType * __restrict actData = &m_ActToSeqHash[handle];
  380. // go through each sequence and give actmod matches preference. LACK of an actmod is a detriment
  381. int top_score = -1;
  382. CUtlVector<int> topScoring( actData->count, actData->count );
  383. for ( int i = 0; i < actData->count; i++ )
  384. {
  385. SequenceTuple * __restrict sequenceInfo = m_pSequenceTuples + actData->startingIdx + i;
  386. int score = 0;
  387. int num_seq_modifiers = sequenceInfo->iNumActivityModifiers;
  388. for ( int k = 0; k < num_seq_modifiers; k++ )
  389. {
  390. bool bFoundMod = false;
  391. for ( int m = 0; m < iModifierCount; m++ )
  392. {
  393. if ( sequenceInfo->pActivityModifiers[ k ] == pActivityModifiers[ m ] )
  394. {
  395. bFoundMod = true;
  396. }
  397. }
  398. if ( bFoundMod )
  399. {
  400. score += 2;
  401. }
  402. else
  403. {
  404. score--;
  405. }
  406. }
  407. if ( score >= top_score )
  408. {
  409. if ( score > top_score )
  410. {
  411. topScoring.RemoveAll();
  412. }
  413. for ( int n=0; n<sequenceInfo->weight; n++ )
  414. {
  415. topScoring.AddToTail( sequenceInfo->seqnum );
  416. }
  417. top_score = score;
  418. }
  419. }
  420. // randomly pick between the highest scoring sequences ( NOTE: this method of selecting a sequence ignores activity weights )
  421. //if ( IsInPrediction() )
  422. //{
  423. // return topScoring[ SharedRandomInt( "SelectWeightedSequence", 0, topScoring.Count() - 1 ) ];
  424. //}
  425. if ( topScoring.Count() )
  426. {
  427. return topScoring[ RandomInt( 0, topScoring.Count() - 1 ) ];
  428. }
  429. else
  430. {
  431. return -1;
  432. }
  433. }
  434. #endif
  435. int SelectHeaviestSequence( CStudioHdr *pstudiohdr, int activity )
  436. {
  437. if ( !pstudiohdr )
  438. return 0;
  439. VerifySequenceIndex( pstudiohdr );
  440. int maxweight = 0;
  441. int seq = ACTIVITY_NOT_AVAILABLE;
  442. int weight = 0;
  443. for (int i = 0; i < pstudiohdr->GetNumSeq(); i++)
  444. {
  445. int curActivity = GetSequenceActivity( pstudiohdr, i, &weight );
  446. if (curActivity == activity)
  447. {
  448. if ( iabs(weight) > maxweight )
  449. {
  450. maxweight = iabs(weight);
  451. seq = i;
  452. }
  453. }
  454. }
  455. return seq;
  456. }
  457. void GetEyePosition ( CStudioHdr *pstudiohdr, Vector &vecEyePosition )
  458. {
  459. if ( !pstudiohdr )
  460. {
  461. Warning( "GetEyePosition() Can't get pstudiohdr ptr!\n" );
  462. return;
  463. }
  464. vecEyePosition = pstudiohdr->eyeposition();
  465. }
  466. //-----------------------------------------------------------------------------
  467. // Purpose: Looks up an activity by name.
  468. // Input : label - Name of the activity to look up, ie "ACT_IDLE"
  469. // Output : Activity index or ACT_INVALID if not found.
  470. //-----------------------------------------------------------------------------
  471. int LookupActivity( CStudioHdr *pstudiohdr, const char *label )
  472. {
  473. VPROF( "LookupActivity" );
  474. if ( !pstudiohdr )
  475. {
  476. return 0;
  477. }
  478. for ( int i = 0; i < pstudiohdr->GetNumSeq(); i++ )
  479. {
  480. mstudioseqdesc_t &seqdesc = pstudiohdr->pSeqdesc( i );
  481. if ( stricmp( seqdesc.pszActivityName(), label ) == 0 )
  482. {
  483. return seqdesc.activity;
  484. }
  485. }
  486. return ACT_INVALID;
  487. }
  488. #if !defined( MAKEXVCD )
  489. //-----------------------------------------------------------------------------
  490. // Purpose: Looks up a sequence by sequence name first, then by activity name.
  491. // Input : label - The sequence name or activity name to look up.
  492. // Output : Returns the sequence index of the matching sequence, or ACT_INVALID.
  493. //-----------------------------------------------------------------------------
  494. int LookupSequence( CStudioHdr *pstudiohdr, const char *label )
  495. {
  496. VPROF( "LookupSequence" );
  497. if (! pstudiohdr)
  498. return 0;
  499. if (!pstudiohdr->SequencesAvailable())
  500. return 0;
  501. //
  502. // Look up by sequence name.
  503. //
  504. int iSequence = pstudiohdr->LookupSequence( label );
  505. if ( iSequence != -1 )
  506. return iSequence;
  507. //
  508. // Not found, look up by activity name.
  509. //
  510. int nActivity = LookupActivity( pstudiohdr, label );
  511. if (nActivity != ACT_INVALID )
  512. {
  513. return SelectWeightedSequence( pstudiohdr, nActivity );
  514. }
  515. return ACT_INVALID;
  516. }
  517. void GetSequenceLinearMotion( CStudioHdr *pstudiohdr, int iSequence, const float poseParameter[], Vector *pVec )
  518. {
  519. if (! pstudiohdr)
  520. {
  521. Msg( "Bad pstudiohdr in GetSequenceLinearMotion()!\n" );
  522. return;
  523. }
  524. if (!pstudiohdr->SequencesAvailable())
  525. return;
  526. if( iSequence < 0 || iSequence >= pstudiohdr->GetNumSeq() )
  527. {
  528. // Don't spam on bogus model
  529. if ( pstudiohdr->GetNumSeq() > 0 )
  530. {
  531. static int msgCount = 0;
  532. while ( ++msgCount < 10 )
  533. {
  534. DevMsg( "Bad sequence (%i out of %i max) in GetSequenceLinearMotion() for model '%s'!\n", iSequence, pstudiohdr->GetNumSeq(), pstudiohdr->pszName() );
  535. }
  536. }
  537. pVec->Init();
  538. return;
  539. }
  540. QAngle vecAngles;
  541. Studio_SeqMovement( pstudiohdr, iSequence, 0, 1.0, poseParameter, (*pVec), vecAngles );
  542. }
  543. float GetSequenceLinearMotionAndDuration( CStudioHdr *pstudiohdr, int iSequence, const float poseParameter[], Vector *pVec )
  544. {
  545. pVec->Init();
  546. if ( !pstudiohdr )
  547. {
  548. Msg( "Bad pstudiohdr in GetSequenceLinearMotion()!\n" );
  549. return 0.0f;
  550. }
  551. if ( !pstudiohdr->SequencesAvailable() )
  552. return 0.0f;
  553. if ( iSequence < 0 || iSequence >= pstudiohdr->GetNumSeq() )
  554. {
  555. // Don't spam on bogus model
  556. if ( pstudiohdr->GetNumSeq() > 0 )
  557. {
  558. static int msgCount = 0;
  559. while ( ++msgCount < 10 )
  560. {
  561. DevMsg( "Bad sequence (%i out of %i max) in GetSequenceLinearMotion() for model '%s'!\n", iSequence, pstudiohdr->GetNumSeq(), pstudiohdr->pszName() );
  562. }
  563. }
  564. return 0.0f;
  565. }
  566. return Studio_SeqMovementAndDuration( pstudiohdr, iSequence, 0, 1.0, poseParameter, (*pVec) );
  567. }
  568. #endif
  569. const char *GetSequenceName( CStudioHdr *pstudiohdr, int iSequence )
  570. {
  571. if( !pstudiohdr || iSequence < 0 || iSequence >= pstudiohdr->GetNumSeq() )
  572. {
  573. if ( pstudiohdr )
  574. {
  575. DevMsg( "Bad sequence in GetSequenceName() for model '%s'!\n", pstudiohdr->pszName() );
  576. }
  577. return "Unknown";
  578. }
  579. mstudioseqdesc_t &seqdesc = pstudiohdr->pSeqdesc( iSequence );
  580. return seqdesc.pszLabel();
  581. }
  582. const char *GetSequenceActivityName( CStudioHdr *pstudiohdr, int iSequence )
  583. {
  584. if( !pstudiohdr || iSequence < 0 || iSequence >= pstudiohdr->GetNumSeq() )
  585. {
  586. if ( pstudiohdr )
  587. {
  588. DevMsg( "Bad sequence in GetSequenceActivityName() for model '%s'!\n", pstudiohdr->pszName() );
  589. }
  590. return "Unknown";
  591. }
  592. mstudioseqdesc_t &seqdesc = pstudiohdr->pSeqdesc( iSequence );
  593. return seqdesc.pszActivityName( );
  594. }
  595. int GetSequenceFlags( CStudioHdr *pstudiohdr, int sequence )
  596. {
  597. if ( !pstudiohdr ||
  598. !pstudiohdr->SequencesAvailable() ||
  599. sequence < 0 ||
  600. sequence >= pstudiohdr->GetNumSeq() )
  601. {
  602. return 0;
  603. }
  604. mstudioseqdesc_t &seqdesc = pstudiohdr->pSeqdesc( sequence );
  605. return seqdesc.flags;
  606. }
  607. //-----------------------------------------------------------------------------
  608. // Purpose:
  609. // Input : *pstudiohdr -
  610. // sequence -
  611. // type -
  612. // Output : Returns true on success, false on failure.
  613. //-----------------------------------------------------------------------------
  614. bool HasAnimationEventOfType( CStudioHdr *pstudiohdr, int sequence, int type )
  615. {
  616. if ( !pstudiohdr || sequence >= pstudiohdr->GetNumSeq() )
  617. return false;
  618. mstudioseqdesc_t &seqdesc = pstudiohdr->pSeqdesc( sequence );
  619. if ( !&seqdesc )
  620. return false;
  621. mstudioevent_t *pevent = GetEventIndexForSequence( seqdesc );
  622. if ( !pevent )
  623. return false;
  624. if (seqdesc.numevents == 0 )
  625. return false;
  626. int index;
  627. for ( index = 0; index < (int)seqdesc.numevents; index++ )
  628. {
  629. if ( pevent[ index ].Event() == type )
  630. {
  631. return true;
  632. }
  633. }
  634. return false;
  635. }
  636. struct animtaglookup_t
  637. {
  638. int nIndex;
  639. const char* szName;
  640. };
  641. #define REGISTER_ANIMTAG( _n ) { _n, #_n },
  642. const animtaglookup_t g_AnimTagLookupTable[ANIMTAG_COUNT] =
  643. {
  644. REGISTER_ANIMTAG( ANIMTAG_UNINITIALIZED )
  645. REGISTER_ANIMTAG( ANIMTAG_STARTCYCLE_N )
  646. REGISTER_ANIMTAG( ANIMTAG_STARTCYCLE_NE )
  647. REGISTER_ANIMTAG( ANIMTAG_STARTCYCLE_E )
  648. REGISTER_ANIMTAG( ANIMTAG_STARTCYCLE_SE )
  649. REGISTER_ANIMTAG( ANIMTAG_STARTCYCLE_S )
  650. REGISTER_ANIMTAG( ANIMTAG_STARTCYCLE_SW )
  651. REGISTER_ANIMTAG( ANIMTAG_STARTCYCLE_W )
  652. REGISTER_ANIMTAG( ANIMTAG_STARTCYCLE_NW )
  653. REGISTER_ANIMTAG( ANIMTAG_AIMLIMIT_YAWMIN_IDLE )
  654. REGISTER_ANIMTAG( ANIMTAG_AIMLIMIT_YAWMAX_IDLE )
  655. REGISTER_ANIMTAG( ANIMTAG_AIMLIMIT_YAWMIN_WALK )
  656. REGISTER_ANIMTAG( ANIMTAG_AIMLIMIT_YAWMAX_WALK )
  657. REGISTER_ANIMTAG( ANIMTAG_AIMLIMIT_YAWMIN_RUN )
  658. REGISTER_ANIMTAG( ANIMTAG_AIMLIMIT_YAWMAX_RUN )
  659. REGISTER_ANIMTAG( ANIMTAG_AIMLIMIT_YAWMIN_CROUCHIDLE )
  660. REGISTER_ANIMTAG( ANIMTAG_AIMLIMIT_YAWMAX_CROUCHIDLE )
  661. REGISTER_ANIMTAG( ANIMTAG_AIMLIMIT_YAWMIN_CROUCHWALK )
  662. REGISTER_ANIMTAG( ANIMTAG_AIMLIMIT_YAWMAX_CROUCHWALK )
  663. REGISTER_ANIMTAG( ANIMTAG_AIMLIMIT_PITCHMIN_IDLE )
  664. REGISTER_ANIMTAG( ANIMTAG_AIMLIMIT_PITCHMAX_IDLE )
  665. REGISTER_ANIMTAG( ANIMTAG_AIMLIMIT_PITCHMIN_WALKRUN )
  666. REGISTER_ANIMTAG( ANIMTAG_AIMLIMIT_PITCHMAX_WALKRUN )
  667. REGISTER_ANIMTAG( ANIMTAG_AIMLIMIT_PITCHMIN_CROUCH )
  668. REGISTER_ANIMTAG( ANIMTAG_AIMLIMIT_PITCHMAX_CROUCH )
  669. REGISTER_ANIMTAG( ANIMTAG_AIMLIMIT_PITCHMIN_CROUCHWALK )
  670. REGISTER_ANIMTAG( ANIMTAG_AIMLIMIT_PITCHMAX_CROUCHWALK )
  671. REGISTER_ANIMTAG( ANIMTAG_FLASHBANG_PASSABLE )
  672. REGISTER_ANIMTAG( ANIMTAG_WEAPON_POSTLAYER )
  673. };
  674. int IndexFromAnimTagName( const char* szName )
  675. {
  676. int i;
  677. for ( i=1; i<ANIMTAG_COUNT; i++ )
  678. {
  679. const animtaglookup_t *pAnimTag = &g_AnimTagLookupTable[i];
  680. if ( !V_strcmp( szName, pAnimTag->szName ) )
  681. {
  682. return pAnimTag->nIndex;
  683. }
  684. }
  685. return ANIMTAG_INVALID;
  686. }
  687. float GetFirstSequenceAnimTag( CStudioHdr *pstudiohdr, int sequence, int nDesiredTag, float flStart, float flEnd )
  688. {
  689. if ( !pstudiohdr || sequence >= pstudiohdr->GetNumSeq() )
  690. return flStart;
  691. mstudioseqdesc_t &seqdesc = pstudiohdr->pSeqdesc( sequence );
  692. if (seqdesc.numanimtags == 0 )
  693. return flStart;
  694. mstudioanimtag_t *panimtag = NULL;
  695. int index;
  696. for ( index = 0; index < (int)seqdesc.numanimtags; index++ )
  697. {
  698. panimtag = seqdesc.pAnimTag(index);
  699. if ( panimtag->tag == ANIMTAG_INVALID )
  700. continue;
  701. if ( panimtag->tag == ANIMTAG_UNINITIALIZED )
  702. {
  703. panimtag->tag = IndexFromAnimTagName( panimtag->pszTagName() );
  704. }
  705. if ( panimtag->tag == nDesiredTag && panimtag->cycle >= flStart && panimtag->cycle < flEnd )
  706. {
  707. return panimtag->cycle;
  708. }
  709. }
  710. return flStart;
  711. }
  712. float GetAnySequenceAnimTag( CStudioHdr *pstudiohdr, int sequence, int nDesiredTag, float flDefault )
  713. {
  714. if ( !pstudiohdr || sequence >= pstudiohdr->GetNumSeq() )
  715. return flDefault;
  716. mstudioseqdesc_t &seqdesc = pstudiohdr->pSeqdesc( sequence );
  717. if (seqdesc.numanimtags == 0 )
  718. return flDefault;
  719. mstudioanimtag_t *panimtag = NULL;
  720. int index;
  721. for ( index = 0; index < (int)seqdesc.numanimtags; index++ )
  722. {
  723. panimtag = seqdesc.pAnimTag(index);
  724. if ( panimtag->tag == ANIMTAG_INVALID )
  725. continue;
  726. if ( panimtag->tag == ANIMTAG_UNINITIALIZED )
  727. {
  728. panimtag->tag = IndexFromAnimTagName( panimtag->pszTagName() );
  729. }
  730. if ( panimtag->tag == nDesiredTag )
  731. {
  732. return panimtag->cycle;
  733. }
  734. }
  735. return flDefault;
  736. }
  737. int GetAnimationEvent( CStudioHdr *pstudiohdr, int sequence, animevent_t *pNPCEvent, float flStart, float flEnd, int index )
  738. {
  739. if ( !pstudiohdr || sequence >= pstudiohdr->GetNumSeq() || !pNPCEvent )
  740. return 0;
  741. mstudioseqdesc_t &seqdesc = pstudiohdr->pSeqdesc( sequence );
  742. if (seqdesc.numevents == 0 || index >= (int)seqdesc.numevents )
  743. return 0;
  744. // Msg( "flStart %f flEnd %f (%d) %s\n", flStart, flEnd, seqdesc.numevents, seqdesc.label );
  745. mstudioevent_t *pevent = GetEventIndexForSequence( seqdesc );
  746. for (; index < (int)seqdesc.numevents; index++)
  747. {
  748. // Don't send client-side events to the server AI
  749. if ( pevent[index].type & AE_TYPE_NEWEVENTSYSTEM )
  750. {
  751. if ( !(pevent[index].type & AE_TYPE_SERVER) )
  752. continue;
  753. }
  754. else if ( pevent[index].Event_OldSystem() >= EVENT_CLIENT ) //Adrian - Support the old event system
  755. continue;
  756. bool bOverlapEvent = false;
  757. if (pevent[index].cycle >= flStart && pevent[index].cycle < flEnd)
  758. {
  759. bOverlapEvent = true;
  760. }
  761. // FIXME: doesn't work with animations being played in reverse
  762. else if ((seqdesc.flags & STUDIO_LOOPING) && flEnd < flStart)
  763. {
  764. if (pevent[index].cycle >= flStart || pevent[index].cycle < flEnd)
  765. {
  766. bOverlapEvent = true;
  767. }
  768. }
  769. if (bOverlapEvent)
  770. {
  771. pNPCEvent->pSource = NULL;
  772. pNPCEvent->cycle = pevent[index].cycle;
  773. #if !defined( MAKEXVCD )
  774. pNPCEvent->eventtime = gpGlobals->curtime;
  775. #else
  776. pNPCEvent->eventtime = 0.0f;
  777. #endif
  778. pNPCEvent->type = pevent[index].type;
  779. pNPCEvent->Event( pevent[index].Event() );
  780. pNPCEvent->options = pevent[index].pszOptions();
  781. return index + 1;
  782. }
  783. }
  784. return 0;
  785. }
  786. int FindTransitionSequence( CStudioHdr *pstudiohdr, int iCurrentSequence, int iGoalSequence, int *piDir )
  787. {
  788. if ( !pstudiohdr )
  789. return iGoalSequence;
  790. if ( !pstudiohdr->SequencesAvailable() )
  791. return iGoalSequence;
  792. if ( ( iCurrentSequence < 0 ) || ( iCurrentSequence >= pstudiohdr->GetNumSeq() ) )
  793. return iGoalSequence;
  794. if ( ( iGoalSequence < 0 ) || ( iGoalSequence >= pstudiohdr->GetNumSeq() ) )
  795. {
  796. // asking for a bogus sequence. Punt.
  797. Assert( 0 );
  798. return iGoalSequence;
  799. }
  800. // bail if we're going to or from a node 0
  801. if (pstudiohdr->EntryNode( iCurrentSequence ) == 0 || pstudiohdr->EntryNode( iGoalSequence ) == 0)
  802. {
  803. *piDir = 1;
  804. return iGoalSequence;
  805. }
  806. int iEndNode;
  807. // Msg( "from %d to %d: ", pEndNode->iEndNode, pGoalNode->iStartNode );
  808. // check to see if we should be going forward or backward through the graph
  809. if (*piDir > 0)
  810. {
  811. iEndNode = pstudiohdr->ExitNode( iCurrentSequence );
  812. }
  813. else
  814. {
  815. iEndNode = pstudiohdr->EntryNode( iCurrentSequence );
  816. }
  817. // if both sequences are on the same node, just go there
  818. if (iEndNode == pstudiohdr->EntryNode( iGoalSequence ))
  819. {
  820. *piDir = 1;
  821. return iGoalSequence;
  822. }
  823. int iInternNode = pstudiohdr->GetTransition( iEndNode, pstudiohdr->EntryNode( iGoalSequence ) );
  824. // if there is no transitionial node, just go to the goal sequence
  825. if (iInternNode == 0)
  826. return iGoalSequence;
  827. int i;
  828. // look for someone going from the entry node to next node it should hit
  829. // this may be the goal sequences node or an intermediate node
  830. for (i = 0; i < pstudiohdr->GetNumSeq(); i++)
  831. {
  832. mstudioseqdesc_t &seqdesc = pstudiohdr->pSeqdesc(i );
  833. if (pstudiohdr->EntryNode( i ) == iEndNode && pstudiohdr->ExitNode( i ) == iInternNode)
  834. {
  835. *piDir = 1;
  836. return i;
  837. }
  838. if (seqdesc.nodeflags)
  839. {
  840. if (pstudiohdr->ExitNode( i ) == iEndNode && pstudiohdr->EntryNode( i ) == iInternNode)
  841. {
  842. *piDir = -1;
  843. return i;
  844. }
  845. }
  846. }
  847. // this means that two parts of the node graph are not connected.
  848. DevMsg( 2, "error in transition graph: %s to %s\n", pstudiohdr->pszNodeName( iEndNode ), pstudiohdr->pszNodeName( pstudiohdr->EntryNode( iGoalSequence ) ));
  849. // Go ahead and jump to the goal sequence
  850. return iGoalSequence;
  851. }
  852. bool GotoSequence( CStudioHdr *pstudiohdr, int iCurrentSequence, float flCurrentCycle, float flCurrentRate, int iGoalSequence, int &nNextSequence, float &flNextCycle, int &iNextDir )
  853. {
  854. if ( !pstudiohdr )
  855. return false;
  856. if ( !pstudiohdr->SequencesAvailable() )
  857. return false;
  858. if ( ( iCurrentSequence < 0 ) || ( iCurrentSequence >= pstudiohdr->GetNumSeq() ) )
  859. return false;
  860. if ( ( iGoalSequence < 0 ) || ( iGoalSequence >= pstudiohdr->GetNumSeq() ) )
  861. {
  862. // asking for a bogus sequence. Punt.
  863. Assert( 0 );
  864. return false;
  865. }
  866. // bail if we're going to or from a node 0
  867. if (pstudiohdr->EntryNode( iCurrentSequence ) == 0 || pstudiohdr->EntryNode( iGoalSequence ) == 0)
  868. {
  869. iNextDir = 1;
  870. flNextCycle = 0.0;
  871. nNextSequence = iGoalSequence;
  872. return true;
  873. }
  874. int iEndNode = pstudiohdr->ExitNode( iCurrentSequence );
  875. // Msg( "from %d to %d: ", pEndNode->iEndNode, pGoalNode->iStartNode );
  876. // if we're in a transition sequence
  877. if (pstudiohdr->EntryNode( iCurrentSequence ) != pstudiohdr->ExitNode( iCurrentSequence ))
  878. {
  879. // are we done with it?
  880. if (flCurrentRate > 0.0 && flCurrentCycle >= 0.999)
  881. {
  882. iEndNode = pstudiohdr->ExitNode( iCurrentSequence );
  883. }
  884. else if (flCurrentRate < 0.0 && flCurrentCycle <= 0.001)
  885. {
  886. iEndNode = pstudiohdr->EntryNode( iCurrentSequence );
  887. }
  888. else
  889. {
  890. // nope, exit
  891. return false;
  892. }
  893. }
  894. // if both sequences are on the same node, just go there
  895. if (iEndNode == pstudiohdr->EntryNode( iGoalSequence ))
  896. {
  897. iNextDir = 1;
  898. flNextCycle = 0.0;
  899. nNextSequence = iGoalSequence;
  900. return true;
  901. }
  902. int iInternNode = pstudiohdr->GetTransition( iEndNode, pstudiohdr->EntryNode( iGoalSequence ) );
  903. // if there is no transitionial node, just go to the goal sequence
  904. if (iInternNode == 0)
  905. {
  906. iNextDir = 1;
  907. flNextCycle = 0.0;
  908. nNextSequence = iGoalSequence;
  909. return true;
  910. }
  911. int i;
  912. // look for someone going from the entry node to next node it should hit
  913. // this may be the goal sequences node or an intermediate node
  914. for (i = 0; i < pstudiohdr->GetNumSeq(); i++)
  915. {
  916. mstudioseqdesc_t &seqdesc = pstudiohdr->pSeqdesc(i );
  917. if (pstudiohdr->EntryNode( i ) == iEndNode && pstudiohdr->ExitNode( i ) == iInternNode)
  918. {
  919. iNextDir = 1;
  920. flNextCycle = 0.0;
  921. nNextSequence = i;
  922. return true;
  923. }
  924. if (seqdesc.nodeflags)
  925. {
  926. if (pstudiohdr->ExitNode( i ) == iEndNode && pstudiohdr->EntryNode( i ) == iInternNode)
  927. {
  928. iNextDir = -1;
  929. flNextCycle = 0.999;
  930. nNextSequence = i;
  931. return true;
  932. }
  933. }
  934. }
  935. // this means that two parts of the node graph are not connected.
  936. DevMsg( 2, "error in transition graph: %s to %s\n", pstudiohdr->pszNodeName( iEndNode ), pstudiohdr->pszNodeName( pstudiohdr->EntryNode( iGoalSequence ) ));
  937. return false;
  938. }
  939. void SetBodygroupPreset( CStudioHdr *pstudiohdr, int& body, char const *szName )
  940. {
  941. if (!pstudiohdr)
  942. return;
  943. for ( int i=0; i<pstudiohdr->GetNumBodyGroupPresets(); i++ )
  944. {
  945. const mstudiobodygrouppreset_t *pBodygroupPreset = pstudiohdr->GetBodyGroupPreset( i );
  946. if ( !V_strcmp( szName, pBodygroupPreset->pszName() ) )
  947. {
  948. for ( int j=0; j<pstudiohdr->numbodyparts(); j++ )
  949. {
  950. mstudiobodyparts_t *pbodypart = pstudiohdr->pBodypart( j );
  951. int iMask = (pBodygroupPreset->iMask / pbodypart->base) % pbodypart->nummodels;
  952. if ( iMask == 1 )
  953. {
  954. int iCurrent = (body / pbodypart->base) % pbodypart->nummodels;
  955. int iValCurrent = (pBodygroupPreset->iValue / pbodypart->base) % pbodypart->nummodels;
  956. body = (body - (iCurrent * pbodypart->base) + (iValCurrent * pbodypart->base));
  957. }
  958. }
  959. break;
  960. }
  961. }
  962. }
  963. void SetBodygroup( CStudioHdr *pstudiohdr, int& body, int iGroup, int iValue )
  964. {
  965. if (! pstudiohdr)
  966. return;
  967. if (iGroup >= pstudiohdr->numbodyparts())
  968. return;
  969. mstudiobodyparts_t *pbodypart = pstudiohdr->pBodypart( iGroup );
  970. if (iValue >= pbodypart->nummodels)
  971. return;
  972. int iCurrent = (body / pbodypart->base) % pbodypart->nummodels;
  973. body = (body - (iCurrent * pbodypart->base) + (iValue * pbodypart->base));
  974. }
  975. int GetBodygroup( CStudioHdr *pstudiohdr, int body, int iGroup )
  976. {
  977. if (! pstudiohdr)
  978. return 0;
  979. if (iGroup >= pstudiohdr->numbodyparts())
  980. return 0;
  981. mstudiobodyparts_t *pbodypart = pstudiohdr->pBodypart( iGroup );
  982. if (pbodypart->nummodels <= 1)
  983. return 0;
  984. int iCurrent = (body / pbodypart->base) % pbodypart->nummodels;
  985. return iCurrent;
  986. }
  987. const char *GetBodygroupName( CStudioHdr *pstudiohdr, int iGroup )
  988. {
  989. if ( !pstudiohdr)
  990. return "";
  991. if (iGroup >= pstudiohdr->numbodyparts())
  992. return "";
  993. mstudiobodyparts_t *pbodypart = pstudiohdr->pBodypart( iGroup );
  994. return pbodypart->pszName();
  995. }
  996. const char *GetBodygroupPartName( CStudioHdr *pstudiohdr, int iGroup, int iPart )
  997. {
  998. if ( !pstudiohdr)
  999. return "";
  1000. if (iGroup >= pstudiohdr->numbodyparts())
  1001. return "";
  1002. mstudiobodyparts_t *pbodypart = pstudiohdr->pBodypart( iGroup );
  1003. if ( iPart < 0 && iPart >= pbodypart->nummodels )
  1004. return "";
  1005. return pbodypart->pModel( iPart )->name;
  1006. }
  1007. int FindBodygroupByName( CStudioHdr *pstudiohdr, const char *name )
  1008. {
  1009. if ( !pstudiohdr )
  1010. return -1;
  1011. int group;
  1012. for ( group = 0; group < pstudiohdr->numbodyparts(); group++ )
  1013. {
  1014. mstudiobodyparts_t *pbodypart = pstudiohdr->pBodypart( group );
  1015. if ( !Q_strcasecmp( name, pbodypart->pszName() ) )
  1016. {
  1017. return group;
  1018. }
  1019. }
  1020. return -1;
  1021. }
  1022. int GetBodygroupCount( CStudioHdr *pstudiohdr, int iGroup )
  1023. {
  1024. if ( !pstudiohdr )
  1025. return 0;
  1026. if (iGroup >= pstudiohdr->numbodyparts())
  1027. return 0;
  1028. mstudiobodyparts_t *pbodypart = pstudiohdr->pBodypart( iGroup );
  1029. return pbodypart->nummodels;
  1030. }
  1031. int GetNumBodyGroups( CStudioHdr *pstudiohdr )
  1032. {
  1033. if ( !pstudiohdr )
  1034. return 0;
  1035. return pstudiohdr->numbodyparts();
  1036. }
  1037. int GetSequenceActivity( CStudioHdr *pstudiohdr, int sequence, int *pweight )
  1038. {
  1039. if (!pstudiohdr || !pstudiohdr->SequencesAvailable() )
  1040. {
  1041. if (pweight)
  1042. *pweight = 0;
  1043. return 0;
  1044. }
  1045. Assert(sequence >= 0 && sequence < pstudiohdr->GetNumSeq());
  1046. mstudioseqdesc_t &seqdesc = pstudiohdr->pSeqdesc( sequence );
  1047. if (!(seqdesc.flags & STUDIO_ACTIVITY))
  1048. {
  1049. SetActivityForSequence( pstudiohdr, sequence );
  1050. }
  1051. if (pweight)
  1052. *pweight = seqdesc.actweight;
  1053. return seqdesc.activity;
  1054. }
  1055. void GetAttachmentLocalSpace( CStudioHdr *pstudiohdr, int attachIndex, matrix3x4_t &pLocalToWorld )
  1056. {
  1057. if ( attachIndex >= 0 )
  1058. {
  1059. const mstudioattachment_t &pAttachment = pstudiohdr->pAttachment(attachIndex);
  1060. MatrixCopy( pAttachment.local, pLocalToWorld );
  1061. }
  1062. }
  1063. //-----------------------------------------------------------------------------
  1064. // Purpose:
  1065. // Input : *pstudiohdr -
  1066. // *name -
  1067. // Output : int
  1068. //-----------------------------------------------------------------------------
  1069. int FindHitboxSetByName( CStudioHdr *pstudiohdr, const char *name )
  1070. {
  1071. if ( !pstudiohdr )
  1072. return -1;
  1073. for ( int i = 0; i < pstudiohdr->numhitboxsets(); i++ )
  1074. {
  1075. mstudiohitboxset_t *set = pstudiohdr->pHitboxSet( i );
  1076. if ( !set )
  1077. continue;
  1078. if ( !stricmp( set->pszName(), name ) )
  1079. return i;
  1080. }
  1081. return -1;
  1082. }
  1083. //-----------------------------------------------------------------------------
  1084. // Purpose:
  1085. // Input : *pstudiohdr -
  1086. // setnumber -
  1087. // Output : char const
  1088. //-----------------------------------------------------------------------------
  1089. const char *GetHitboxSetName( CStudioHdr *pstudiohdr, int setnumber )
  1090. {
  1091. if ( !pstudiohdr )
  1092. return "";
  1093. mstudiohitboxset_t *set = pstudiohdr->pHitboxSet( setnumber );
  1094. if ( !set )
  1095. return "";
  1096. return set->pszName();
  1097. }
  1098. //-----------------------------------------------------------------------------
  1099. // Purpose:
  1100. // Input : *pstudiohdr -
  1101. // Output : int
  1102. //-----------------------------------------------------------------------------
  1103. int GetHitboxSetCount( CStudioHdr *pstudiohdr )
  1104. {
  1105. if ( !pstudiohdr )
  1106. return 0;
  1107. return pstudiohdr->numhitboxsets();
  1108. }